<?php

namespace RxConnexionBundle\Service;

use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;

Class HL7Service {

    private $em;
    private $container;
    public $segments;
    public $messageTypes;

    public function __construct(EntityManager $em, ContainerInterface $container) {
        $this->em = $em;
        $this->container = $container;
        $this->segments = array(
            'DG1', // Diagnosis
            'EVN', // Event Type
            'GT1', // Guarantor
            'IN1', // Insurance
            'MSH', // Message Header
            'MSA', // Message Acknowledgment
            'NK1', // Next of kin/associated parties
            'NTE', // Notes and comments
            'OBR', // Observation request
            'OBX', // Observation result
            'ORC', // Common order
            'PID', // Patient identification
            'FT1', // (for DFT messages) – Financial transaction
        );
        $this->messageTypes = array(
            array('code' => 'ADT-A01', 'type_code' => 'A01',  'label' => 'Patient Admit', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'DG1', 'IN1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A02', 'type_code' => 'A02', 'label' => 'Patient Transfer', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A04', 'type_code' => 'A04', 'label' => 'Patient Registration', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'DG1', 'IN1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A05', 'type_code' => 'A05', 'label' => 'Patient Pre-admission', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'DG1', 'IN1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A08', 'type_code' => 'A01', 'label' => 'Patient Information Update', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'DG1', 'IN1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A11', 'type_code' => 'A11', 'label' => 'Cancel Patient Admit', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A12', 'type_code' => 'A12', 'label' => 'Cancel Patient Transfer', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO
                'displayGlobal' => true,
            )),
            array('code' => 'ADT-A13', 'type_code' => 'A13', 'label' => 'Cancel Patient Discharge', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'DG1', 'IN1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'BAR-P01', 'type_code' => 'P01', 'label' => 'Add Billing Account', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'BAR-P02', 'type_code' => 'P02', 'label' => 'Purge Billing Account', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'BAR-P05', 'type_code' => 'P05', 'label' => 'Update Billing Account', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => true,
            )),
            array('code' => 'DFT-P03', 'type_code' => 'P03', 'label' => 'Detail Financial Transaction', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'FT1'), // SEQUENCE MUST BE CORRECT ALSO
                'displayGlobal' => false,
            )),
            array('code' => 'MDM-T01', 'type_code' => 'T01', 'label' => 'Medical document management', 'settings' => array(
                'requiredSegments' => array('MSH', 'EVN', 'PID', 'PV1', 'TXA'), // SEQUENCE MUST BE CORRECT ALSO 
                'displayGlobal' => false,
            )),
            /*array('code' => 'ORM-O01', 'type_code' => 'O01', 'label' => 'Order (Pharmacy/treatment)', 'settings' => array(
                'requiredSegments' => array('MSH', 'PID', 'ORC'), // SEQUENCE MUST BE CORRECT ALSO 
            )),
            array('code' => 'ACK', 'label' => 'General Acknowledgement'),*/
            /*'BAR', // Add/change billing account
            'DFT', // Detailed financial transaction
            'MDM', // Medical document management
            'MFN', // Master files notification
            'ORM', // Order (Pharmacy/treatment)
            'ORU', // Observation result (unsolicited)
            'QRY', // Query, original mode
            'RAS', // Pharmacy/treatment administration
            'RDE', // Pharmacy/treatment encoded order
            'RGV', // Pharmacy/treatment give
            'SIU', // Scheduling information unsolicited*/
        );
    }

    public function generate($typeCode, $patient, $params){
        $messageType = $this->message_type_by_type_code($typeCode);
        $segments = $this->generate_patient_segment($messageType, $patient, $params);
        return array('success' => true, 'data' => implode("\r\n", $segments));
    }

    private function message_type_by_type_code($typeCode) {
        foreach ($this->messageTypes as $messageType) {
            if ($messageType['code'] == $typeCode) {
                return $messageType;
            }
        }
        return null;
    }

    private function generate_patient_segment($messageType, $patient, $params){
        $requiredSegments = $messageType['settings']['requiredSegments'];

        $segmetDetails = array();
        foreach($requiredSegments as $segment){
            $functionName = preg_replace("/[\- _]/", '_', $segment);
            $segmetDetails[] = $this->$functionName($messageType, $patient, $params);
        }

        return $segmetDetails;
    }




    /*--------------------------------------------------------------------------------------------------------*/
    /*				           	SEGMENT GENERATOR   						    						       */
    /*--------------------------------------------------------------------------------------------------------*/

    private function MSH($messageType,$patient, $params){
        $msh_sendingApplication = array(
            'RXCON', // namespace identification ex. company name
            '', // universal identification
            '', // universal identification type
        );
        $msh_sendingFacility = array(
            '', // namespace identification
            '', // universal identification
            '', // universal identification type
        );
        $msh_receivingApplication = array(
            '', // namespace identification
            '', // universal identification
            '', // universal identification type
        );
        $msh_receivingFacility = array(
            '', // namespace identification
            '', // universal identification
            '', // universal identification type
        );
        $msh_dateTime = date('YmdHis');
        $security = '';
        $msh_messageType = array(
            preg_replace("/[\- _]/", '^', $messageType['code']), // message code ex. ACK for acknowledgment
            '', // trigger event
            '', // message structure
        );
        $msh_messageControlId = 'RXCON'.time();
        $msh_processingId = array(
            'P', // processing id ex. D-debuggin, P-production, T-training
            '', // processing mode ex. A-Archive, I-initial load, Not present-current processing, R-Restore from archive, T-scheduled/currenty processing
        );
        $msh_versionId = '2.7.1'; // 2.7.1 is the latest in hl7 version 2.x
        $msh = implode('|', array(
            'MSH',
            '^~\&',
            implode('^', array_filter($msh_sendingApplication)),
            implode('^', array_filter($msh_sendingFacility)),
            implode('^', array_filter($msh_receivingApplication)),
            implode('^', array_filter($msh_receivingFacility)),
            $msh_dateTime,
            $security,
            implode('^', array_filter($msh_messageType)),
            $msh_messageControlId,
            implode('^', array_filter($msh_processingId)),
            $msh_versionId,
        )); // produces something like this "MSH|^~\&|rxconnexion||||20190606131820||ACK|47L9Mc9K7wbRAUs19qf9GTeM|P|2.7.1";
        return $msh;
    }

    private function EVN($messageType, $patient, $params){
        return implode('|', array(
            'EVN',
            $messageType['type_code'],
            date('YmdHis')
        ));
    }

    private function MSA($messageType, $patient, $params){
        $msa_acknowledgementCode = 'AA'; // ex. AA-Application Accept, AE-Application Error, AR-Application Reject, CA-Commit Accept, CE-Commit Error, CR-Commit Reject
        $msa_messageControlId = 'RXCON'.time();
        $msa_textMessage = 'Sample Acknowledgment'; //  your message (optional)
        $msa = implode('|', array(
            'MSA',
            $msa_acknowledgementCode,
            $msa_messageControlId,
            $msa_textMessage,
        ));
        return $msa;
    }

    private function PID($messageType, $patient, $params){
        $dttst = new DateTimeToStringTransformer();
        $bdate =  !empty($patient->getBirthDate()) ? date('YmdHis', strtotime($dttst->transform($patient->getBirthDate()))) : '';
        return implode('|', array(
            'PID',
            '', // SET ID
            $patient->getId(), // Patient ID
            implode('^', array(
                $patient->getAlbertaHealthCardNo() == '' ? 'null' : $patient->getAlbertaHealthCardNo(), // health card number
                '', // identifier check digit
                '', // check digit scheme
                '', // assigning auth
                'HC', // identifier code (type) HC = Health Card
            )), // Patient identifier
            '', // Alternate Patient Id
            implode('^', array(
                $patient->getLastName(), // last name
                $patient->getFirstName(), // first name
                $patient->getMiddleName(), // middle name
            )), // Patient Name
            '', // mothers maiden name
            $bdate, // date of birth
            $patient->getGender() == 'Male' ? 'M' : 'F', // gender
            '', // patient alias
            '', // race
            implode('^', array(
                implode('&', array(
                    $patient->getStreet(), // street
                )), // street address
                '', // other designation
                $patient->getCity()->getDescription(), // city
                '', // province
                $patient->getPostalCode()->getDescription(), // zip code
            )), // patient address
        ));
    }

    private function PV1($messageType, $patient, $params){
        $referringProviders = array();
        $patientReferringProviders = $patient->getPatientReferringProviders();
        $i = 0;
        foreach($patientReferringProviders as $patientReferringProvider){
            $i++;
            $referringProviders[] = implode('^', array(
                $patientReferringProvider->getReferringProvider()->getId(), // set id
                $patientReferringProvider->getReferringProvider()->getLastName(), // Last name
                $patientReferringProvider->getReferringProvider()->getFirstName(), // First name
                $patientReferringProvider->getReferringProvider()->getMiddleName(), // First name
            ));
        }
        return implode('|', array(
            'PV1',
            '', // set id
            'I', // patient identifier I=in patient,
            '', // assigned patient location (floor/room in hospital)
            '', // Admission type A=accident, C=elective, E=emergency etc.
            '', // Pre admit number
            '', // Prior patient location
            '', // attending doctor
            implode('~', $referringProviders), // referring doctor
            '', // consulting doctor
            'MED', // Hospital service CAR=cardiac service, MED=medical service, PUL=pulmonary, SUR=surgical, URO=urology service
        ));
    }

    private function DG1($messageType, $patient, $params){
        $dg1 = array();
        $patientDiagnosiss = $patient->getDiagnoses();
        $i = 0;
        foreach($patientDiagnosiss as $patientDiagnosis){
            $i++;
            $dg1[] = implode('|', array(
                'DG1',
                $i, // set id
                '', // Diagnosis coding method
                'null', // Diagnosis code
                $patientDiagnosis->getDiagnosis()->getDescription(), // diagnosis desc
                '', // Diagnosis date time
                'F', // Diagnosis type A=admitting, F=final, W=working
            ));
        }
        return join("\r\n", $dg1);
    }

    private function IN1($messageType, $patient, $params){
        $dttst = new DateTimeToStringTransformer();
        $in1 = array();
        $patientInsurances = $patient->getPatientInsurances();
        $i = 0;
        foreach($patientInsurances as $patientInsurance){
            $i++;
            $in1[] = implode('|', array(
                'IN1',
                $i, // set id
                'null', // Insurance plan id
                implode('^', array(
                    $patientInsurance->getId(), // ID
                    '', // Check Digit
                    '', // Code identifying
                    '', // Assigning auth
                    'U', // identifier type U=unspecified identifer
                    '', // Assigning facility
                    !empty($patientInsurance->getStartDate()) ? date('YmdHis', strtotime($dttst->transform($patientInsurance->getStartDate()))) : '' , // Effective date
                    !empty($patientInsurance->getExpireDate()) ? date('YmdHis', strtotime($dttst->transform($patientInsurance->getExpireDate()))) : '' , // Expiration date
                )), // Insurance company id
                $patientInsurance->getInsurance()->getDescription(), // Insurance company name
            ));
        }
        return join("\r\n", $in1);
    }

    private function FT1($messageType, $patient, $params){
        $patientBilling = $this->em->getRepository('RxConnexionBundle:PatientBillingEntity')->find($params['tx_id']);
        $patientBillingBreakdowns = $patientBilling->getPatientBillingBreakdowns();
        $ft1 = array();
        foreach($patientBillingBreakdowns as $patientBillingBreakdown){
            $priceType = '';
            switch ($patientBillingBreakdown->getType()){
                case "Chart Procedure": $priceType = "PF";break;
                case "Item": $priceType = "DC";break;
                case "Other": $priceType = "IC";break;
            }
            $ft1[] = implode('|', array(
                'FT1',
                '', // set id
                $params['tx_id'], // tran id
                $patientBillingBreakdown->getId(), // transaction batch id
                date('YmdHis'),
                '', // posting date
                'PY', // transaction type ex AJ=adjustment, CD=credit, CG=charge, CO=co-payment, PY=payment
                'null', // transaction code,
                '', // transaction desc
                '', // tran desc alt
                '', // tran quantity
                '', // tran amount extended
                implode('^', array(
                    implode('&', array(
                        $patientBillingBreakdown->getQuantity(), // quantity
                        $patientBillingBreakdown->getPrice(), // denomination
                    )), // price
                    $priceType , // price type
                ))
            ));
        }

        return join("\r\n", $ft1);
    }

    private function TXA($messageType, $patient, $params){
        return implode('|', array(
            'TXA',
            '1', // set id
            'PR', // document type
            '', // content presentation
            '', // activity date
            '', // activity provider code/name
            '', // Origination date
            '', // Transcription date
            '', // edit date
            '', // Originator code
            '', // Assigned document auth
            '', // transcriptionist code/name
            $params['fileId'], // Document number (id)
            '', // parent document number
            '', // place order number
            '', // Filler order number
            $params['fileName'], // File name
            'DO', // Doc status ex DO=documented, AU=authenticated, DI=dictated, IN=incomplete, iP=in progress, etc
        ));
    }

}