<?php 
/*
  Conjuro-Web, Easy web builder: Core library

  Copyright (c) 2022 XWolf Override

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

  include_once "ezDBclass.php";
  
  class Conjuro{
    private $db;
    private $conf;
    private $modlues=[];
    private $controls=[];
    private $basePath;

    function __construct($conf) {
      $this->conf=$conf;
      //$this->loadControls();
    }
  
    // Return data from database
    public function DBget($type,$key=""){
      if ($key!="")
        $data=$this->db()->select($type,null,'where id=?','i',$key);
      else
        $data=$this->db()->select($type);
      foreach($data as $i => $row){
        $data[$i]['$type']=$type;
        $types=[];
        foreach($row as $k => $v){
          if ($v===null)
            $types[$k]='!';
          else
            $types[$k]=is_string($v)?'s':'i';
        }
        $data[$i]['$types']=$types;
      }
      return $data;
    }

    public function DBsave($data){
      $type=$data['$type'];
      if ($type=='') // data is not entity
        return;
      $id=$data['id'];
      $dbdata=$this->db()->select($type,null,'where id=?','i',$id);
      if (count($dbdata)==0){
        $fields='';
        $types='';
        $values=[];
        foreach($data as $i => $v){
          if ($i[0]=='$')
            continue;
          if (strlen($fields)>0)
            $fields.=',';
          $fields.=$i;
          $types.=$data['$types'][$i];
          $values[]=$v;
        }
        if ($fields!=''){
          $this->db->insert($type,$fields,$types,...$values);
          $data['id']=$this->db->insert_id;
          $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'INSERT',$type,$this->db->insert_id);
        }
      }else{
        $dbdata=$dbdata[0]; // Select the first row
        $set='';
        $types='';
        $values=[];
        foreach($data as $i => $v){
          if ($i[0]=='$')
            continue;
          if ($dbdata[$i]!=$v){// only differences
            if (strlen($set)>0)
              $set.=',';
            $set.="$i = ?";
            $types.=$data['$types'][$i];
            $values[]=$v;
          }
        }
        $types.='i';
        $values[]=$id;
        if ($set!=''){
          $this->db->update($type,$set,'where id=?',$types,...$values);   
          $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'UPDATE',$type,$id);
        }
      }
      return $data;
    }

    // Delete an entity, use DBdelete(<entity>) or DBdelete(<entity_type>,<id>)
    public function DBdelete($data,$id=null){
      if (is_string($data)){
        if ($id){
          $type=$data;
        }
      }else{
        $type=$data['$type'];
        if ($type=='') // data is not entity
          return;
        $id=$data['id'];
      }
      $this->db()->delete($type,'where id=?','i',$id);
      $this->db->insert('user-log','user,action,entity,entity_id','sssi',$_SESSION['user']['user'],'DELETE',$type,$id);
      return $data;
    }

    // Database
    public function db(){
      if(!isset($this->db))
        $this->db=new DB($conf);
      return $this->db;
    }

    //STATIC: loads a JSON
    public static function loadJson($path){
      $json=file_get_contents($path);
      return json_decode($json, true);
    }

    //////////////////////////////////////////////////// MODULE HANDLING
    ////////////////////////////////////////////////////////////////////

    /**
     * Returns a loaded module or load
     */
    public function module($name){
      if (!isset($name) || strpos($name,".")!==false || strpos($name,"/")!==false){
        throw new Exception("Module name error: '$name'.");
      }
      if (!isset($this->modules[$name])){
        $conf=$this->conf;
        $m=include "content/modules/$name.php";
        if ($m===false)
          throw new Exception("Module '$name' does not exists.");
        if (method_exists($m,"init")){
          $m->init($this);
        }
        $this->modules[$name]=$m;
      }
      return $this->modules[$name];
    }

    /**
     * Return the module name knowing the instance
     */
    public function getModuleName($mod){
      foreach($this->modules as $k => $v)
        if ($v===$mod)
          return $k;
      return null;
    }

    /////////////////////////////////////////////////// CONTROL HANDLING
    ////////////////////////////////////////////////////////////////////

    /**
     * Load a control library
     */
    public function loadControlLibrary($name){
      if (!isset($name) || strpos($name,".")!==false || strpos($name,"/")!==false){
        throw new Exception("Name error");
      }
      $ctrls = include "content/controls/$name.php";
      foreach($ctrls as $ctrl){
        $cname=$ctrl->getName();
        if (isset($controls[$cname]))
          throw new Exception("Control '$cname' already defined loading library '$name'.");
          $this->controls[$cname]=$ctrl;
      }
    }

    /**
     * Load control libraries installed
     */
    /*public function loadControls(){
      // TODO- glob directoty to fetch all libraries.
      $this->loadControlLibrary("common");
    }*/

    /**
     * Return a control
     */
    public function control($name){
      if (isset($this->controls[$name]))
        return $this->controls[$name];
      return new ErrorControl($name);
    }

    /**
     * Return the available controls.
     */
    public function listControls(){
      $arr=[];
      foreach($this->controls as $k => $ctrl){
        $arr[]=$k;
      }
      return $arr;
    }
  }

  /**
   * Base class for module implementation
   */
  abstract class ModuleBase{
    private $mapi=[];
    protected $conf;

    function __construct($conf) {
      $this->conf=$conf;
    }

    protected function register($name,$function){
      $this->mapi[$name]=$function;
    }

    protected function deregister($name){
      unset($this->mapi[$name]);
    }

    public function do($name="",&...$attrs){
      if (!$name)
        $name="main";
      if (!isset($this->mapi[$name])){
        $modname=c()->getModuleName($this);
        throw new Exception("Action '$name' does not exist in module '$modname'.");
      }
      return $this->mapi[$name](...$attrs);
    }
  }

  /** Class for module implementation */
  class Module extends ModuleBase{

  }

  /**
   * Base class for controls
   */
  abstract class Control{
    private $name;
    function __construct($name) {
      $this->name=$name;
    }

    public function getName(){
      return $this->name;
    }

    public function render($meta=null,$data=null){
      $this->renderControl($meta,$data);
    }

    abstract protected function renderControl($meta,$data);
  }

  /**
   * Error control class
   */
  class ErrorControl extends Control{
    protected function renderControl($meta,$data){
      $name = $this->getName();
      echo "['$name' not found]";
    }
  }

  function C(){
    global $c;
    if (!isset($c)){
      $cfg=include "Config.php";
      if($cfg["debug"]){
        error_reporting(E_ALL ^ E_NOTICE);
        ini_set('error_reporting', E_ALL ^ E_NOTICE);
        ini_set('display_errors',1);    
      }
      $c=new Conjuro($cfg);
    }
    return $c;
  }

  function M($name){
    return C()->module($name);
  }

?>