<?php

/*
Requirements:
Meta information: Modules, Lists, Items, Menu, Icons, Localization
Permissions ?
Actions:
* List: Add, Remove
* Item: Add, Edit, Delete

Possible protocols: XML-RPC
How to transfer table data efficiently, binary protocol, compression, encryption
Multi queries in single command

*/

class ModuleAPI extends Module
{
  function __construct(System $System)
  {
    parent::__construct($System);
    $this->Name = 'API';
    $this->Version = '1.0';
    $this->Creator = 'Chronos';
    $this->License = 'GNU/GPLv3';
    $this->Description = 'Remote API for support of other clients';
    $this->Dependencies = array(ModuleUser::GetName());
    $this->Models = array(APIToken::GetClassName());
  }

  function DoStart(): void
  {
    $this->System->RegisterPage(['api'], 'PageAPI');
  }
}

class ApiToken extends Model
{
  static function GetModelDesc(): ModelDesc
  {
    $Desc = new ModelDesc(self::GetClassName());
    $Desc->AddReference('User', User::GetClassName());
    $Desc->AddString('Token');
    return $Desc;
  }
}

class PageAPI extends Page
{
  public string $DataFormat;

  function __construct(System $System)
  {
    parent::__construct($System);
    $this->RawPage = true;
    $this->DataFormat = '';
  }

  function Show(): string
  {
    // p - token
    if (array_key_exists('p', $_GET)) {
      $Token = $_GET['p'];
      $DbResult = $this->Database->query('SELECT `User` FROM `APIToken` WHERE `Token`="'.$Token.'"');
      if ($DbResult->num_rows > 0)
      {
        $DbRow = $DbResult->fetch_assoc();
        $this->UserId = $DbRow['User'];
      } else die('Wrong token');
    } else die('Missing access token');
    // f - data format
    if (array_key_exists('f', $_GET)) $this->DataFormat = $_GET['f'];
      else $this->DataFormat = 'php';
    // a - action
    if (array_key_exists('a', $_GET)) $Action = $_GET['a'];
      else $Action = '';
    // t - table
    if (array_key_exists('t', $_GET)) $Table = $_GET['t'];
      else $Table = '';
    // i - index of item
    if (array_key_exists('i', $_GET)) $ItemId = $_GET['i'];
      else $ItemId = 0;

    if ($Action == 'list') $Output = $this->ShowList($Table, $ItemId);
      else $Output = 'Unsupported action';

    return $Output;
  }

  function ShowList(string $Table, $ItemId): string
  {
    if (($Table != '') and (array_key_exists($Table, $this->System->FormManager->Classes)))
      $FormClass = $this->System->FormManager->Classes[$Table];
      else return 'Table not found';

    if (array_key_exists('SQL', $FormClass))
      $SourceTable = '('.$FormClass['SQL'].') AS `TX`';
      else $SourceTable = '`'.$FormClass['Table'].'` AS `TX`';

    $Filter = '';
    if ($ItemId != 0)
    {
      $Filter .= '`Id`='.$ItemId;
    }
    if ($Filter != '') $Filter = ' WHERE '.$Filter;

    $Result = array();
    $DbResult = $this->Database->query('SELECT * FROM '.$SourceTable.$Filter);
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Result[] = $DbRow;
    }
    if ($this->DataFormat == 'php') $Output = serialize($Result);
      else if ($this->DataFormat == 'xml') $Output = $this->array2xml($Result);
      else if ($this->DataFormat == 'json') $Output = json_encode($Result);
      else die('Unknown data format '.$this->DataFormat);
    return $Output;
  }

  function array2xml($array, $node_name = "root")
  {
    $dom = new DOMDocument('1.0', 'UTF-8');
    $dom->formatOutput = true;
    $root = $dom->createElement($node_name);
    $dom->appendChild($root);

    $array2xml = function ($node, $array) use ($dom, &$array2xml)
    {
      foreach ($array as $key => $value)
      {
        if ( is_array($value) )
        {
          if (is_numeric($key)) $key = 'N'.$key; //die('XML tag name "'.$key.'" can\'t be numeric');
          $n = $dom->createElement($key);
          $node->appendChild($n);
          $array2xml($n, $value);
        } else {
          if (is_numeric($key)) $key = 'N'.$key; //die('XML attribute name "'.$key.'" can\'t be numeric');
          $attr = $dom->createAttribute($key);
          $attr->value = $value;
          $node->appendChild($attr);
        }
      }
    };
    $array2xml($root, $array);
    return $dom->saveXML();
  }
}

