# $Id: Model.pm,v 1.8 2005/01/21 23:26:26 jkremer Exp $
# Copyright 2004 Sun Microsystems, Inc., All Rights Reserved.
# Sun Proprietary/Confidential: For Internal Use Only
package Catalog::Model;

use strict;
use Catalog::Entry;
use XML::LibXML;
use base 'Catalog::Entry';
use Data::Dumper;


sub cim_mapping
{
   return 
	  {
		ctrl     => 'CIM_Card',
		fcport   => 'CIM_Card',
		fru      => 'CIM_Card',
		device   => 'CIM_Card',
		disk     => 'CIM_Card',
		pcu      => 'CIM_Card',
		sfp      => 'CIM_Card',
		iom      => 'CIM_Card',
		battery  => 'CIM_Card',
		fan      => 'CIM_Card',
		midplane => 'NWS_Midplane',
		volume   => 'NWS_StorageVolume',
		pool     => 'NWS_StorageVolume',
		vdisk    => 'NWS_StorageVolume'
	  }
}

sub getCIMClassName {
  my ($this, $className) = @_;
  return $this->cim_mapping->{$className} || 'CIM_Card';
}

sub getNWSClassName {
  my ($this, $className) = @_;
  return $this->cim_mapping->{$className} || 'CIM_Card';
}

sub getNWSPhysicalClassName {
  my ($this, $className) = @_;
  my $map = { midplane => 'NWS_Midplane' };
  if ($map->{$className}){
	 return $map->{$className};
  }
  return "CIM_Card";
}

sub isLogical {
  my ($this, $className) = @_;
  if (!$this->{class}->{logical}){  # couldn't load model.
	  my $devs = {  ctrl=>1,fcport=>1,disk=>1,pcu=>1,sfp=>1,iom=>1,battery=>1,
					  fan=>1,midplane=>1,volume=>1,pool=>1,vdisk=>1
					};
	 return $devs->{$className};
  }
  return $this->isA($className, "logical");
}

sub isDevice { 
  my ($this, $className) = @_;
  if (!$this->{class}->{device}){  # couldn't load model.
	 my $devs = { ctrl=>1,fcport=>1,disk=>1,pcu=>1,sfp=>1,iom=>1,battery=>1,
					  fan=>1,midplane=>1
					};
	 return $devs->{$className};
  }
  return $this->isA($className, "device");
}

sub isSystem {
  my ($this, $className)= @_;
  if (!$this->{class}->{system}){  # couldn't load model.
	 my $cmap = { host=>1,fcswitch=>1,storade=>1,system=>1 };
	 return $cmap->{$className};
  }
  return $this->isA($className, "system");
}

sub isA {
  my ($this, $isClass, $base) = @_;
  return 1 if ($isClass eq $base);
  my $class = $this->{class}->{$isClass};
  if ($class){
	 my $extends = $class->{extends};
	 if ($extends){
		if (!($isClass eq $extends)){
		  return $this->isA($extends, $base);
		}	
	 }
  }	
  return 0;
}


sub getClass {
  my ($this, $className) = @_;
  return $this->{class}->{$className};
}

#
# Property is a hash = name, type, valueMap
sub getProperty {
  my ($this, $className, $propName) = @_;
  my $class = $this->{class}->{$className};
  my $prop = $class->{property}->{$propName};
  return $prop if ($prop);
  my $super = $class->{extends};
  if ($super && ($super ne $className)){
	 return $this->getProperty($super, $propName);
  }
  return undef;
}

sub getMappedValue {
  my ($this, $className, $propName, $value) = @_;
  my $prop = $this->getProperty( $className, $propName);
  if ($prop){
	 my $valueMap = $prop->{valueMap};
	 return $valueMap->{$value};
  }
}



sub new {
  my ($class, $type, $dir) = @_;

  my $this = { "type"   => $type,
               "entry"  => "model.xml",
					"dir"    => $dir,
					"mp"     => {}
				 };
  bless($this, "Catalog::Model");
  return $this->loadEntry();
}

sub load {
  my ($this) = @_;
  $this->{class} = {};
  $this->_loadFile($this->{file});
}

sub _loadFile {
  my ($this, $file) = @_;

  my $parser = XML::LibXML->new();
  my $dom;
  eval {
	 $dom  = $parser->parse_file($file);
  };
  if ((!$dom)||($@)){
	 $this->setError("Error parsing XML document element. $@");
	 return;
  }
  my $model = $dom->getDocumentElement();
  foreach my $incElem ($model->findnodes("include")){
	 $this->_loadInclude($incElem);
  }
  foreach my $classElement ($model->findnodes("class")){
	 $this->_loadClass($classElement);
  }
}

sub _loadInclude {
  my ($this, $incElem) = @_;

  my $file = $incElem->getAttribute("file");
  my $path = $this->{dir}.$file;

  if (-f $path){
	 $this->_loadFile($path);
  }
  else {
	 $path = $this->getCommonDir() . $file;
	 if (-f $path){
		$this->_loadFile($path);
	 }
	 else {
		$this->setError("Error including file $path.");
	 }
  }
}


sub _loadClass {
  my ($this, $classElement) = @_;
  my $cname = $classElement->getAttribute("name");
  my $class = $this->{class}->{$cname};

  if (!$class){
	 $class = { name     => $cname,
					property => {},
					extends  => $classElement->getAttribute("extends"),
					abstract => $classElement->getAttribute("abstract")
				 };
	 $this->{class}->{$cname} = $class;
  }
  else {
	 $class->{abstract} = $classElement->getAttribute("abstract");
  }	

  foreach my $pelem ($classElement->findnodes("property")){
	 $this->_loadProperty($class, $pelem);
  }
}

sub _loadProperty {
  my ($this, $class, $pelem) = @_;
  my $pname    = $pelem->getAttribute("name");
  my $prop     = {};
  foreach my $att ($pelem->attributes()) {
	 $prop->{$att->name()} = $att->value();
  }
  my $vref = $prop->{valueRef};
  if ($vref){
	 $prop->{value}    = $this->{value}->{$vref};
	 $prop->{valueMap} = $this->{valueMap}->{$vref};
  }
  else {
	 my $value    = [];
	 my $valueMap = {};
	 foreach my $val ($pelem->findnodes("value")){
		my $map = $val->getAttribute("map");
		if ($map){
		  $valueMap->{$map} = $val->textContent();
		}
		else {
		  push(@$value, $val->textContent());
		}
	 }
	 $prop->{value}    = $value;
	 $prop->{valueMap} = $valueMap;
	 my $vdef = $prop->{valueDef};
	 if ($vdef){
		$this->{value}->{$vdef}    = $value;
		$this->{valueMap}->{$vdef} = $valueMap;
	 }
  }

  $class->{property}->{$pname} = $prop;
}

sub check {
  my ($this) = @_;
  my $classes = $this->{class};
  my $out = undef;
  foreach my $classname (keys %$classes){
	 next if ("element" eq $classname);
	 next if ("physical" eq $classname);
	 my $class = $classes->{$classname};
	 if (!$class->{extends}){
		$out .= "$classname doesn't extend any class.\n"; #'
	 }
#	 my $properties = $class->{property};
#	 foreach my $p (keys %$properties){
#		my $prop = $properties->{$p};
#		if (!$prop->{type}){
#		  $out .= "$classname $p has no type.\n";
#		}	
#	 }
  }
  return $out;
}

# get the property name from the model.properties for the given
# class and substitute ${VAR} found.
sub localizeProperty {
  my ($this, $repClass, $property, $locale) = @_;
  my $className = $repClass->getClassName();
  my $mp = $this->getModelProperties($locale);

  my $name = $mp->{$className}->{$property};
  while (!$name) {
	 my $class = $this->{class}->{$className};
	 my $extends = $class->{extends};

	 if (!$extends){
		return $property;
	 }
	 if ($extends eq $className){
		return $property;
	 }
	 $className = $extends;
	 $name = $mp->{$className}->{$property};
  }

  # Replace all ${_Name} with values.
  while ($name =~ /.*\$\{(.*)\}.*/){
	 my $argName = $1;
	 my $argValue = $repClass->getProperty($argName) || $argName;
	 $name =~ s/\$\{$argName\}/$argValue/g;
  }

  return $name;
}

# get the property name from the model.properties for the given class name
# and property.
sub getPropertyName {
  my ($this, $className, $property, $locale) = @_;
  my $mp = $this->getModelProperties($locale);

  my $name = $mp->{$className}->{$property};
  while (!$name) {
	 my $class = $this->{class}->{$className};
	 my $extends = $class->{extends};

	 if (!$extends){
		return $property;
	 }
	 if ($extends eq $className){
		return $property;
	 }
	 $className = $extends;
	 $name = $mp->{$className}->{$property};
  }
  return $name;
}


sub getModelProperties {
  my ($this, $locale) = @_;
  $locale = "en" if (!$locale);
  my $mp = $this->{mp};
  return $mp->{$locale} if ($mp->{$locale});
  my $props = $this->_loadProperties($locale);
  if ($props){
	 $mp->{$locale} = $props;
	 return $props;
  }
}

sub _loadProperties {
  my ($this, $locale) = @_;
  my ($file, $stdFile);
  if (!$locale || $locale eq "en"){
	 $file    = $this->{dir}."/model.properties";
	 $stdFile = $this->getCommonDir() ."/std_model.properties";
  }
  else {
	 $file    = $this->{dir}."/model_".$locale.".properties";
	 $stdFile = $this->getCommonDir() ."/std_model_".$locale.".properties";
  }
  my %mprops;

  $this->loadPropFile($stdFile, \%mprops);
  $this->loadPropFile($file, \%mprops);
  return \%mprops;
}

sub loadPropFile {
  my ($this, $file, $map) = @_;
  $map->{HEADER} = {} if (!$map->{HEADER});
  my $prop = $map->{HEADER};

  if ($file){
	 open(O, "$file");
	 my @L = <O> ; close(O);
	 foreach my $line (@L) {
		chomp $line;
		next if (index($line, "#") == 0);
		if (index($line, "[") == 0){
		  if ($line =~ /\[(.*)\]/){
			 my $section = $1;
			 $prop = $map->{$section};
			 if (!$prop){
				$map->{$section} = {};
				$prop = $map->{$section};
			 }
			 next;
		  }	
		}
		my $sindex = index($line, "=");
		next if ($sindex <= 0);
		$prop->{ substr($line, 0, $sindex) } = substr($line, $sindex+1);
	 }
  }
}

sub outModelProperties {
  my ($this, $locale) = @_;
  $locale = "en" if (!$locale);
  my $mp = $this->getModelProperties($locale);
  my $out;
  foreach my $section (keys %$mp){
	 my $values = $mp->{$section};
	 foreach my $name (keys %$values){
		my $value = $values->{$name};
#		if ("HEADER" eq $section){
		  $out .= $name . "=" . $value ."\n";
#		}
#		else {
#		  $out .= $section . "." . $name . "=" . $value ."\n";
#		}	
	 }
  }
  return $out;
}

sub genCode {
  my ($this, $args) = @_;

  my $classes = $this->{class};
  my $package = $args->{package} ||
	 "com.sun.netstorage.fm.storade.resource.report";

  foreach my $className (keys %$classes){
	 my $class = $classes->{$className};
	 my $extends = $class->{extends} || "ReportClass";
	 open (J, ">$className.java");
	 print J "package $package;\n";
	 print J "public class $className extends $extends\n{\n";
	 print J "  public $className(){\n";
	 print J "    super(\"$className\");\n";
	 print J "  }\n";
	 print J "  public $className(String name){\n";
	 print J "    super(\"$className\", name);\n";
	 print J "  }\n";

	 my $props = $class->{property};
	 foreach my $pname (keys %$props){
		my $p = $props->{pname};
		print J "  public String get$pname(){\n";
		print J "    return getProperty(\"$pname\");\n";
	   print J "  }\n";
		print J "  public void set$pname(String value){\n";
		print J "    setProperty(\"$pname\", value);\n";
		print J "  }\n";
	 }

	 print J "} // class $className\n";
	 close (J);
  }
}

1;
