package Opals::SIF::SifSubscriber;

# Version number
$VERSION   = 0.01;


use warnings;
use strict;
use XML::XPath;
use XML::LibXML;
use Opals::SIF::SIF_MsgFactory;
use Opals::SIF::HTTPService;
use Opals::SIF::AckRegisterMsgHandler;
use Opals::SIF::Specs;
use Opals::SIF::SIFObjectDataParser;



my $sifMsgFatory= Opals::SIF::SIF_MsgFactory->new();
my $httpSvc = Opals::SIF::HTTPService->new();
my $sifObjectParser=Opals::SIF::SIFObjectDataParser->new();
sub new {
    my ($class,$sifInfo,$zone,$eventProcessFn) = @_;
    my $self={
            sifInfo=>$sifInfo,
            zone=>$zone,
            regiestered=>0,
            subscribeTo=>undef,
            eventProcessFn=>sub{print "no event handling callback function\n";}
        };
    if(defined $eventProcessFn){
        $self->{'eventProcessFn'}=$eventProcessFn;
    }
    bless $self, $class;
    return $self;
}

#################################################################

sub init{
    my ($self)=@_;
    my $ret=0;
    if($self->register() && defined $self->{"acl"}->{'SIF_Subscribe'}){
        if($self->subscribe()){
            $ret=1;
        }
    }

    return $ret;
}
#################################################################
sub register{
    my($self)=@_;
    my $regMsg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_Register");
    my $ackMsg= $httpSvc->post($self->{'zone'},$regMsg->getXml());
    #print " $ackMsg\n";
    my $rs =processAckMsg($ackMsg);
    my $success=1;
    if($rs->{'status'} eq "0"){
        $self->{"registered"}=1;
        $self->{"acl"}=getAcl($ackMsg);
    }
    else{
        $success=0;
        #log register error
    }
    return $success;

}
#################################################################
sub unRegister{
    my($self,$zone)=@_;
    my $msg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_UnRegister");
    my $rs= $httpSvc->post($self->{'zone'},$msg->getXml());

}

#################################################################
sub subscribe{
    my($self)=@_;
    my $msg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_Subscribe");
    my $ackMsg= $httpSvc->post($self->{'zone'},$msg->getXml());
    my $rs =processAckMsg($ackMsg);
    if($rs->{'status'} eq "0"){
        $self->{"subscribed"}=1;;
    }


}
#################################################################
sub unSubscribe{
    my($self,$zone)=@_;
    my $regMsg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_Unsubscribe");
    my $ackMsg= $httpSvc->post($self->{'zone'},$regMsg->getXml());

}
#################################################################
sub requestMessage{
    my($self,$objName,$cond)=@_;
    my $queryInfo={objectName=>$objName};
    if(defined $cond){
        $queryInfo->{'cond'}=$cond;
    }
    my $reqMsg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_Request",$queryInfo);
    my $rs= $httpSvc->post($self->{'zone'},$reqMsg->getXml());

    #print "QUERY:\n",$reqMsg->getXml(), "\n";
    return $rs;

}

#################################################################
sub getMessage{
    my($self,$zone)=@_;
    my $xml=undef;
    my $msg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_GetMessage");
    my $ackMsg= $httpSvc->post($self->{'zone'},$msg->getXml());
    #    print "============\n$ackMsg\n";
    my $rs =processAckMsg($ackMsg);
    if($rs->{'status'} eq "0"){
        my $xmlObj =XML::XPath->new(xml=>$ackMsg);
        ($xml)=$xmlObj->findnodes_as_string(XPATH_GET_MSG);
    #    print "============\n$xml\n";
        #open DEBUG ,">/tmp/std.xml";  print DEBUG "============\n$xml\n";close DEBUG;
        my $msgObj=$self->handleGetMsg($xml);
        $rs->{'eventObj'} =$msgObj->{'eventObj'};
        $rs->{'responseObjList'} =$msgObj->{'responseObjList'};
        #my $packetNumber =$xmlObj->getNodeText(XPATH_RESPONSE_PACKETNUMBER);
        #my $morePackets  =$xmlObj->getNodeText(XPATH_RESPONSE_MOREPACKETS);
        #$self->handleGetMsg($xml);
    }
    return $rs;
}

#################################################################
sub isRegistered{
    my($self)=@_;
    return $self->{'registered'};
}
#################################################################
sub hasAccess{
    my($self,$access,$dataObjName)=@_;
    return $self->{'acl'}->{$access}->{$dataObjName};
}

#################################################################
#
# Reference http://specification.sifinfo.org/Implementation/2.4/Messaging.html#AgentMessageHandlingProtocols
#
sub handleGetMsg{
    my ($self,$xml)=@_;
    my $sifErr=undef;
    my $ackInfo={};
    my $morePackets=0;
    my $retVal={eventObj=>undef,responseObjList=>undef};
    my $parser   = XML::LibXML->new();
    eval { $parser->parse_string($xml); };
    if($@){
        # STEP 2: XML not well-formed  
        $sifErr={SIF_Cateogry=>1,SIF_Code=>2};
    }

    else{
         my $xmlObj =XML::XPath->new(xml=>$xml);
         my $version="1.1";
         if($xml =~ m/<SIF_MsgId>(.*?)<\/SIF_MsgId>/g){
            $ackInfo->{'originalMsgId'}=$1;
         }
         if($xml =~ m/<SIF_SourceId>(.*?)<\/SIF_SourceId>/g){
            $ackInfo->{'originalSourceId'}=$1;
         }

         if(my ($node) =$xmlObj->findnodes("/SIF_Message")){
             $version=$node->getAttribute("Version");
         }
        # STEP 5: check if SIF version supported
         if(!defined SIF_SUPPORT_VERSION->{$version}){
            # STEP 6: not supported
            $sifErr={SIF_Cateogry=>12,SIF_Code=>3};
         }
         elsif(!validateXmlMsg($xml)){ #STEP 7
            #STEP 8
            $sifErr={SIF_Cateogry=>1,SIF_Code=>3};
         }
        # STEP 9
         elsif(!$xmlObj->exists(XPATH_RESPONSE_MSG) &&
               !$xmlObj->exists(XPATH_EVENT_MSG)){
            #STEP 10
            $sifErr={SIF_Cateogry=>12,SIF_Code=>2};
         }
         else{
             
            # STEP 11: Handling Event/Response message
            if($xmlObj->exists(XPATH_EVENT_MSG)){
                my ($eventXml)=$xmlObj->findnodes_as_string(XPATH_EVENT_OBJECT);
                $retVal->{'eventObj'} =processEvent($eventXml,$self->{'sifInfo'}->{'version'});
            }
            elsif($xmlObj->exists(XPATH_RESPONSE_MSG)){
                my $packetNumber =$xmlObj->getNodeText(XPATH_RESPONSE_PACKETNUMBER);
                my ($objXml)=$xmlObj->findnodes_as_string(XPATH_RESPONSE_OBJECT);
                $retVal->{'responseObjList'}=processResponse($objXml,$self->{'sifInfo'}->{'version'});

            }
            #my $stdInfo=$stdInfoParser->parse($stdObjXml,$version);
            $ackInfo->{'SIF_Code'}=1;
         }
         if(defined $sifErr){
            $ackInfo->{'error'}=$sifErr;
         }
         my $ackMsg =$sifMsgFatory->createMessage($self->{'sifInfo'},"SIF_Ack",$ackInfo);
         my $zisAckMsg= $httpSvc->post($self->{'zone'},$ackMsg->getXml());
    }
    return $retVal
}
#################################################################
sub processEvent{
    my ($xml,$version)=@_;
    my $xmlObj =XML::XPath->new(xml=>$xml);
    my $event=undef;
    my $objType=$xmlObj->getNodeText("/SIF_ObjectData/SIF_EventObject/\@ObjectName"); 
    my ($objXml)=$xmlObj->findnodes_as_string("/SIF_ObjectData/SIF_EventObject/$objType");
    my $action=$xmlObj->findnodes("/SIF_ObjectData/SIF_EventObject/\@Action");
    my $objData=$sifObjectParser->parse($objXml,$version);
    $event={action=>$action,objectType=>$objType,objectData=>$objData};
    return $event;

}

#################################################################
sub processResponse{
    my ($xml,$version)=@_;
    my $xmlObj =XML::XPath->new(xml=>$xml);
    my $rsObjList=[];
    my $objType="";
    for my $name (qw(StudentPersonal SchoolInfo RoomInfo StaffPersonal StudentSchoolEnrollment)){
        if($xmlObj->exists("/SIF_ObjectData/$name")){
            $objType=$name;
            last;
        }
    }
    if($objType ne ""){
        foreach my $node($xmlObj->findnodes("/SIF_ObjectData/$objType")){
            my $objData=$sifObjectParser->parse($node->toString(),$version);
            push @$rsObjList,{objectType=>$objType ,objectData=>$objData};
        }
    }
    return  $rsObjList;
    

}
#################################################################
sub validateXmlMsg{
    my ($xml)=@_;
#to be implimented
#REF:http://specification.sifinfo.org/Implementation/2.4/CodeSets.html#InfrastructureErrorCategoryType
    return 1;
}
#################################################################

sub processAckMsg{
    my ($xmlMsg)=@_;
    my $parser   = XML::LibXML->new();
    eval { $parser->parse_string($xmlMsg); };
    my $rs={};
    if($@){
        $rs = {status=>0, error=>{desc=>"Invalid XML"}};
    }
    else{
        my $xmlObj =XML::XPath->new(xml=>$xmlMsg);
        if($xmlObj->exists(XPATH_ACK_ERROR)){
            my $error={code     =>$xmlObj->getNodeText(XPATH_ACK_ERROR_CODE),
                        category=>$xmlObj->getNodeText(XPATH_ACK_ERROR_CATEGORY),
                        desc    =>$xmlObj->getNodeText(XPATH_ACK_ERROR_DESC),
                        extDesc =>$xmlObj->getNodeText(XPATH_ACK_ERROR_EXTENDEDDESC)
                        };
            $rs = {status=>-1, error=>$error};

        }
        elsif($xmlObj->exists(XPATH_ACK_STATUS_CODE)){
            my $code=$xmlObj->getNodeText(XPATH_ACK_STATUS_CODE);
            $rs->{'status'} =$code;
            if ($code eq "0" || $code eq "0"){
                $rs->{'error'}=undef;
            }
            elsif($code eq "7"){
                $rs->{'error'}={desc=>"duplicate SIF_MsgId"};
            }
            elsif($code eq "8"){
                 $rs->{'error'}={desc=>"ZIS is asleep"};

            }
            else{
                $rs->{'error'}={desc=>"UNKNOW ERROR"};
            }

        }
        else{
            $rs = {status=>-1, error=>{desc=>"UNKNOW ERROR"}};
        }

    }
        
    return $rs;
   
}
############################################################################################
sub getAcl{
    my ($xmlMsg) =@_;

    my @accessCtrl= qw(
    SIF_ProvideAccess
    SIF_SubscribeAccess 
    SIF_PublishAddAccess 
    SIF_PublishChangeAccess
    SIF_PublishDeleteAccess
    SIF_RequestAccess
    SIF_RespondAccess);
    my $acl={}; 
    my $xpath= XPATH_ACK_STATUS_DATA ."/SIF_AgentACL" ;
    my $xmlObj =XML::XPath->new(xml=>$xmlMsg);

    if($xmlObj->exists($xpath)){
       foreach my $ac (@accessCtrl){
           foreach my $node($xmlObj->findnodes("$xpath/$ac/SIF_Object")){
                my $objName= $node->getAttribute("ObjectName"); 
                $acl->{$ac}->{$objName}=1;
            }
       }
    }
    return $acl;

}


#################################################################
1;

