package MpmasQLModelElementAbstract;
use strict;
use warnings;

use Exporter;
use Storable qw(dclone);
use Data::Dumper;
use SimpleObject;

use CtUtilities qw(is_in_list is_in_string_list stub_in_list);
use Carp qw(cluck carp croak);


our @ISA = qw(SimpleObject);

### class globals
my $donotsetdefaults = 0;
my $defaultvaluefunc = \&get_default_value;
my $ismultiperiod = 0;
my $coeffWithConstraints = 0;
my $default_standard_milp = 10;

### abstract methods
sub calculate_attributes {}
sub defaultclass {};
sub list_of_default_fields {}
sub list_of_attribute_fields {
	return [qw(othermilp othermilp_subelements)]
}
sub list_of_attribute_stubs {}
sub get_default_value {};
sub set_default_value {};


	


### constructor
sub create {
	my ($class, $mn,  $fields, $subelements, $othersubelements, $verbose, $standard_milp, $templateref, $context) = @_;
	$verbose = 0 unless (defined $verbose);
	my $self = {};
	$self->{id} = $mn;
	bless $self, $class;
	$self->set_standard_milp($standard_milp) if (defined $standard_milp);
	#unless ($donotsetdefaults) {
	#	for my $i (@{$self->list_of_default_fields}) {
	#		if (not exists $fields->{$i}){
	#			my $temp = $self->get_default_value($i);
	#			print	"Field $i set to default value $temp in  $mn \n" if $verbose;
	#			$fields->{$i} = $temp;
	#		}
    #	}
	#}
	
	for my $j (keys %{$fields}) {
		if (is_in_string_list($j,$self->list_of_attribute_fields)) {
			$self->{$j} = $fields->{$j};
		}
		else {
			my $c = 0;
			for my $k (@{$self->list_of_attribute_stubs}) {
				
				if ($j =~ /^$k(\d+)/) {			
					$self->{$k}->{$1} = $fields->{$j};
					$c = 1;
				}
			}
			$self->{others}->{$j} =$fields->{$j} unless ($c);
		}
	}
	$self->{subelements} = dclone($subelements) if (defined $subelements);
	$self->{othermilp_subelements} = dclone($othersubelements) if (defined $othersubelements);
	$self->calculate_attributes unless $donotsetdefaults;
	
	$self->{templateref} = $templateref if (defined $templateref);
	$self->{context} = $context if (defined $context);
	
	return $self;
}
###switches for using default values or not - affects all instances of subclasses
sub switch_off_defaults {
	#shift; #ignore invoker
	$donotsetdefaults = 1;
}
sub switch_on_defaults {
	#shift; #ignore invoker
	$donotsetdefaults = 0;
}
sub set_multiperiod {
	#shift; #ignore invoker
	$ismultiperiod = 1;
}
sub unset_multiperiod {
	#shift; #ignore invoker
	$ismultiperiod = 0;
}
sub  is_multiperiod {
	#shift; #ignore invoker
	return $ismultiperiod ;
}

sub set_coeffWithConstraints {
	#shift; #ignore invoker
	$coeffWithConstraints = 1;
}
sub unset_coeffWithConstraints {
	#shift; #ignore invoker
	$coeffWithConstraints = 0;
}
sub  has_coeffWithConstraints {
	#shift; #ignore invoker
	return $coeffWithConstraints ;
}

##accessor classes
sub set_group {
	my ($self, $classification, $group) = @_;
	$self->{grouping}->{$classification} = $group;
}

sub get_group {
	my ($self, $classification) = @_;
	return $self->{grouping}->{$classification};
}

sub id {
	return $_[0]->{id};
}
sub name {
	return $_[0]->{name} if (exists  $_[0]->{name}  );
    return $_[0]->get_default_value("name") unless ($donotsetdefaults);
    return undef;
}
sub type {
	return $_[0]->{type} if (exists  $_[0]->{type}  );
    return $_[0]->get_default_value("type") unless ($donotsetdefaults);
    return undef;
}
sub attr {
	my ($self, $attr,$milpid) = @_;
	use warnings FATAL => qw/uninitialized numeric/;
	local $SIG{ __DIE__ } = sub { Carp::confess( @_ ) };

	return $self->{othermilp}->{$milpid}->{$attr} if (defined $milpid and $milpid != $self->standard_milp() and defined $self->{othermilp}->{$milpid}->{$attr});
	return $self->{$attr} if (exists $self->{$attr} );
	
    return $self->get_default_value($attr) unless ($donotsetdefaults);
    return undef;
}


sub attr_defined {
	my ($self, $attr,$milpid) = @_;
	use warnings FATAL => qw/uninitialized numeric/;
	local $SIG{ __DIE__ } = sub { Carp::confess( @_ ) };

	return defined ($self->{othermilp}->{$milpid}->{$attr}) if (defined $milpid and $milpid != $self->standard_milp() and exists $self->{othermilp}->{$milpid}->{$attr});
	return  defined $self->{$attr} ;
	return 0;
}
sub set_attr {
	my ($self, $attr, $value, $milpid) = @_;
    return ($self->{othermilp}->{$milpid}->{$attr}  = $value) if (defined $milpid and $milpid != $self->standard_milp() );
	return $self->{$attr} = $value;
}
sub subelements {
    return keys %{$_[0]->{subelements}};
}
sub subelement {
    return $_[0]->{subelements}->{$_[1]};
}
sub delete_subelement {
	delete $_[0]->{subelements}->{$_[1]};
}
sub has_subelement {
	my ($self, $subelement) = @_;
	return exists ($self->{subelements}->{$subelement})
}
sub has_subelement_field {
	my ($self, $subelement, $field) = @_;
	return exists ($self->{subelements}->{$subelement}->{$field})
}
sub add_subelement_field {
	my ($self, $subelement,$field, $value) = @_;
        $self->{subelements}->{$subelement}->{$field} = $value;
}
sub modify_subelement_field {
	my ($self, $subelement,$field, $value) = @_;
	carp STDERR "Subelement $subelement,field $field does not exist\n" unless (exists $self->{subelements}->{$subelement}->{$field});
        $self->{subelements}->{$subelement}->{$field} = $value;
}
sub subelement_fields {
	return () unless (exists $_[0]->{subelements}->{$_[1]});
	return keys %{$_[0]->{subelements}->{$_[1]}};
}
sub subelement_field {
	my ($self, $subelement, $which) = @_;
	carp "Undefined argument to method subelement_field: ($subelement, $which)\n" unless (defined $subelement and defined $which);
	return undef unless (exists $self->{subelements}->{$subelement});
	return $self->{subelements}->{$subelement}->{$which};
}
sub delete_subelement_field {
	my ($self, $subelement, $which) = @_;
	delete $self->{subelements}->{$subelement}->{$which};	
}
sub milpspecific_subelement_field {
	my ($self, $subelement, $which, $milpid) = @_;
	carp "Undefined argument to method subelement_field: ($subelement, $which)\n" unless (defined $subelement and defined $which);
	return undef unless (exists $self->{subelements}->{$subelement});
	return ($self->{othermilp_subelements}->{$milpid}->{$subelement}->{$which}  ) if (defined $milpid and $milpid != $self->standard_milp() and defined $self->{othermilp_subelements}->{$milpid}->{$subelement}->{$which});
	return $self->{subelements}->{$subelement}->{$which};
}
sub set_standard_milp {
	$_[0]->{standard_milp} = $_[1];
}
sub standard_milp {
	return $_[0]->{standard_milp} if exists ( $_[0]->{standard_milp}) ;
	return $default_standard_milp;
}
1;
