<?php

/*
 *
 * Response class definitions
 *
 */

namespace FreePBX\modules\Sccp_manager\aminterface;

// ************************************************************************** Response *********************************************

abstract class Response extends IncomingMessage
{

    protected $_events;
    protected $_completed;
    protected $keys;

    public function __construct($rawContent)
    {

        parent::__construct($rawContent);
        $this->_events = array();
// this logic is false - even if we have an error, we will not get anymore data, so is completed.
        $this->_completed = $this->isSuccess();
    }

    public function __sleep()
    {
        $ret = parent::__sleep();
        $ret[] = '_completed';
        $ret[] = '_events';
        return $ret;
    }

    public function getEvents()
    {
        return $this->_events;
    }
    public function getClosingEvent() {
        return $this->_events['ClosingEvent'];
    }
    public function removeClosingEvent() {
        unset($this->_events['ClosingEvent']);
    }
    public function getCountOfEvents() {
        return count($this->_events);
    }

    public function isSuccess()
    {
        // returns true if response message does not contain error
        return stristr($this->getKey('Response'), 'Error') === false;
    }

    public function isList()
    {
        if ($this->getKey('EventList') === 'start' ) {
            return true;
        }
    }

    public function getMessage()
    {
        return $this->getKey('Message');
    }

    public function setActionId($actionId)
    {
        $this->setKey('ActionId', $actionId);
    }

    public function getVariable($_rawContent, $_fields = '')
    {
        $lines = explode(Message::EOL, $_rawContent);
        foreach ($_fields as $key => $value) {
            foreach ($lines as $data) {
                $_pst = strpos($data, $value);
                if ($_pst !== false) {
                    $this->setKey($key, substr($data, $_pst + strlen($value)));
                }
            }
        }
    }
}
class GenericResponse extends Response
{
}

//****************************************************************************
// There are two types of Response messages returned by AMI
// Self contained responses which include any data requested;
// List Responses which contain the data in event messages that follow
// the response message.Response and Event
// Following are the self contained Response classes.
//****************************************************************************

class Generic_Response extends Response
{
    public function __construct($rawContent)
    {
        // Only used for self contained responses.
        parent::__construct($rawContent);
        // add dummy closing event
        $this->_events['ClosingEvent'] = new ResponseComplete_Event($rawContent);
        $this->_completed = true;
        $this->eventListIsCompleted = true;

    }
}

class Login_Response extends Generic_Response
{
}

class Command_Response extends Generic_Response
{
    private $_temptable;
    public function __construct($rawContent)
    {
        $this->_temptable = array();
        parent::__construct($rawContent);
        $lines = explode(Message::EOL, $rawContent);
        foreach ($lines as $line) {
            $content = explode(':', $line);
            if (is_array($content)) {
                switch (strtolower($content[0])) {
                    case 'actionid':
                        $this->_temptable['ActionID'] = trim($content[1]);
                        break;
                    case 'response':
                        $this->_temptable['Response'] = trim($content[1]);
                        break;
                    case 'privilege':
                        $this->_temptable['Privilege'] = trim($content[1]);
                        break;
                    case 'output':
                        // included for backward compatibility with earlier versions of chan_sccp_b. AMI api does not precede command output with Output
                        $this->_temptable['Output'] = explode(PHP_EOL,str_replace(PHP_EOL.'--END COMMAND--', '',trim($content[1])));
                        break;
                    default:
                        $this->_temptable['Output'] = explode(PHP_EOL,str_replace(PHP_EOL.'--END COMMAND--', '', trim($line)));
                        break;
                }
            }
        }
    }
    public function getResult()
    {
        return $this->_temptable;
    }
}

class SCCPJSON_Response extends Generic_Response
{
    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        $this->getVariable($rawContent, array("DataType" => "DataType:", "JSONRAW" => "JSON:"));
        if (null !== $this->getKey('JSONRAW')) {
            $this->setKey('Response', 'Success');
        }
    }
    public function getResult()
    {
        if (($json = json_decode($this->getKey('JSON'), true)) != false) {
            return $json;
        }
    }
}

//***************************************************************************//
// Following are the Response classes where the data is contained in a series.
// of event messages.

class SCCPGeneric_Response extends Response
{
    protected $_tables;
    private $_temptable;

    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        // Confirm that there is a list following. This overrides any setting
        // made in one of the parent constructs.
        $this->_completed = !$this->isList();
    }

    public function addEvent($event)
    {
        // Start of list is handled by the isList function in the Constructor
        // which also defines the list end event

        if ( empty($thisSetEventEntryType)) {
            // This is empty as soon as we have received a TableStart.
            // The next message is the first of the data sets
            // We use this variable in the switch to add set entries
            if (strpos($event->getName(), 'Entry')) {
                $thisSetEventEntryType = $event->getName();
            } else {
                $thisSetEventEntryType = 'undefinedAsThisIsNotASet';
            }
        }
        // Unknown events will cause an exception.
        // All event classes must be defined within Event.class.
        if (get_class($event) === 'FreePBX\modules\Sccp_manager\aminterface\UnknownEvent') {
            $this->_events[] = $event;
            return;
        }
        switch ( $event->getName()) {
            case $thisSetEventEntryType :
                $this->_temptable['Entries'][] = $event;
                break;
            case 'TableStart':
                //initialise
                $this->_temptable = array();
                $this->_temptable['Name'] = $event->getTableName();
                $this->_temptable['Entries'] = array();
                $thisSetEventEntryType = '';
                break;
            case 'TableEnd':
                //Close
                if (!is_array($this->_tables)) {
                    $this->_tables = array();
                }
                $this->_tables[$event->getTableName()] = $this->_temptable;
                $this->_temptable = array();
                $thisSetEventEntryType = 'undefinedAsThisIsNotASet';

                // Finished the table. Now check to see if everything was received
                // If counts do not match return false and table will not be
                //loaded
                if ($event->getKey('TableEntries') != count($this->_tables[$event->getTableName()]['Entries'])) {
                    return false;
                }
                break;
            //case $eventListEndEvent;
            case $this->getKey('eventListEndEvent');
                // Have the list end event. The correct number of entries is verified in the event constructor
                $this->_events['ClosingEvent'] = $event;
                $this->eventListEndEvent = null;
                //return $this->_completed = true;
                break;
            default:
                // add regular list event
                $this->_events[] = $event;
        }
    }

    protected function ConvertTableData( $_tablename, array $_fkey, array $_fields)
    {
        $result = array();
        $_rawtable = $this->Table2Array($_tablename);
        // Check that there is actually data to be converted
        if (empty($_rawtable)) { return $result;}
        foreach ($_rawtable as $_row) {
            $all_key_ok = true;
            // No need to test if $_fkey is array as array required
            foreach ($_fkey as $_fid) {
                if (empty($_row[$_fid])) {
                    $all_key_ok = false;
                } else {
                    $set_name[$_fid] = $_row[$_fid];
                }
            }
            $Data = &$result;

            if ($all_key_ok) {
                foreach ($set_name as $value_id) {
                    $Data = &$Data[$value_id];
                }
                // Label converter in case labels and keys are different
                foreach ($_fields as $value_key => $value_id) {
                    $Data[$value_id] = $_row[$value_key];
                }
            }
        }
        return $result;
    }

    protected function ConvertEventData(array $_fkey, array $_fields)
    {
        $result = array();

        foreach ($this->_events as $_row) {
            $all_key_ok = true;
            $tmp_result = $_row->getKeys();
            $set_name = array();
            // No need to test if $_fkey is arrray as array required
            foreach ($_fkey as $_fid) {
                if (empty($tmp_result[$_fid])) {
                    $all_key_ok = false;
                } else {
                    $set_name[$_fid] = $tmp_result[$_fid];
                }
            }
            $Data = &$result;
            if ($all_key_ok) {
                foreach ($set_name as $value_id) {
                    $Data = &$Data[$value_id];
                }
                // Label converter in case labels and keys are different - not actually required.
                foreach ($_fields as $value_id) {
                    $Data[$value_id] = $tmp_result[$value_id];
                }
            }
        }
        return $result;
    }

    public function Table2Array( $tablename )
    {
        $result =array();
        if (empty($tablename) || !is_array($this->_tables)) {
            return $result;
        }
        foreach ($this->_tables[$tablename]['Entries'] as $trow) {
            $result[]= $trow->getKeys();
        }
        return $result;
    }

    public function getResult()
    {
            return $this->getMessage();
    }
}



class SCCPShowSoftkeySets_Response extends SCCPGeneric_Response
{
    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        $this->setKey('eventlistendevent', 'SCCPShowSoftKeySetsComplete');
    }
    public function getResult()
    {
        return $this->ConvertTableData(
            'SoftKeySets',
            array('set','mode'),
            array('description'=>'description','label'=>'label','lblid'=>'lblid')
            );
    }
}

class SCCPShowDevices_Response extends SCCPGeneric_Response
{
    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        $this->setKey('eventlistendevent', 'SCCPShowDevicesComplete');
    }
    public function getResult()
    {
        return $this->ConvertTableData(
            'Devices',
            array('mac'),
            array('mac'=>'name','address'=>'address','descr'=>'descr','regstate'=>'status',
                  'token'=>'token','act'=>'act', 'lines'=>'lines','nat'=>'nat','regtime'=>'regtime')
            );
    }
}

class SCCPShowDevice_Response extends SCCPGeneric_Response
{
    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        $this->setKey('eventlistendevent', 'SCCPShowDeviceComplete');
    }
    public function getResult()
    {
        // This object has a list of events _events, and a list of tables _tables.
        $result = array();

        foreach ($this->_events as $trow) {
                $result = array_merge($result, $trow->getKeys());
        }
        // Now handle label changes so that keys from AMI correspond to db keys in _tables
        $result['Buttons'] = $this->ConvertTableData(
            'Buttons',
            array('id'),
            array('id'=>'id','channelobjecttype'=>'channelobjecttype','inst'=>'inst',
                  'typestr'=>'typestr', 'type'=>'type', 'pendupdt'=>'pendupdt', 'penddel'=>'penddel', 'default'=>'default'
                  )
        );
        $result['SpeeddialButtons'] = $this->ConvertTableData(
            'SpeeddialButtons',
            array('id'),
            array('id'=>'id','channelobjecttype'=>'channelobjecttype','name'=>'name','number'=>'number','hint'=>'hint')
        );
        $result['CallStatistics'] = $this->ConvertTableData(
            'CallStatistics',
            array('type'),
            array('type'=>'type','channelobjecttype'=>'channelobjecttype','calls'=>'calls','pcktsnt'=>'pcktsnt','pcktrcvd'=>'pcktrcvd',
                  'lost'=>'lost','jitter'=>'jitter','latency'=>'latency', 'quality'=>'quality','avgqual'=>'avgqual','meanqual'=>'meanqual',
                  'maxqual'=>'maxqual', 'rconceal'=>'rconceal', 'sconceal'=>'sconceal'
                  )
        );
        $result['SCCP_Vendor'] = array('vendor' => strtok($result['skinnyphonetype'], ' '), 'model' => strtok('('),
                                       'model_id' => strtok(')'), 'vendor_addon' => strtok($result['configphonetype'], ' '),
                                       'model_addon' => strtok(' '));
        if (empty($result['SCCP_Vendor']['vendor']) || $result['SCCP_Vendor']['vendor'] == 'Undefined') {
            $result['SCCP_Vendor'] = array('vendor' => 'Undefined', 'model' => $result['configphonetype'],
                                          'model_id' => '', 'vendor_addon' => $result['SCCP_Vendor']['vendor_addon'],
                                          'model_addon' => $result['SCCP_Vendor']['model_addon']
                                          );
        }
        $result['MAC_Address'] =$result['macaddress'];
        return $result;
    }
}

class ExtensionStateList_Response extends SCCPGeneric_Response
{
    public function __construct($rawContent)
    {
        parent::__construct($rawContent);
        $this->setKey('eventlistendevent', 'ExtensionStateListComplete');
    }
    public function getResult()
    {
        $result = $this->ConvertEventData(array('exten','context'), array('exten','context','hint','status','statustext'));
        return $result;
    }
}
