<?php

class Model {
    public $data;
    public $id = false;
    public $redis;

    function __construct()
    {
        //redis
        if ($GLOBALS['redis'])
            $GLOBALS['redis'] = $GLOBALS['redis'];
    }

    /**
     * Magic getter function
     *
     * @param $name Variable name
     *
     * @return mixed
     */
    public function __get($name)
    {
        if (isset($this->data[$name]))
            return $this->data[$name];
    }

    /**
     * Magic setter function
     *
     * @return mixed
     */
    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function getDBFields()
    {
        return $this->dbFields;
    }

    /**
     * Returns all data fields but removes filtered datapoints like passwords, etc
     */
    public function getDataFiltered()
    {
        $data = $this->data;
        if(is_array($this->hidden))
            foreach($this->hidden as $secretfield)
                unset($data[$secretfield]);
        foreach($this->dbFields as $field=>$options)
            if($options['type']=='csv')
                $data[$field] = ($data[$field]?explode(';',$data[$field]):[]);
        return $data;
    }

    public function getField($field,$id=false)
    {
        if(!$id)
            $id = $this->id;
        return $GLOBALS['redis']->hget($this->dbTable.':'.$id,$field);
    }

    public function save()
    {
        $newgen = false;
        if (!$this->id)
        {
            $this->id = gen_ulid();
            $newgen = true;
        }
        if (!$this->validate($newgen))
            return false;
        foreach($this->dbFields as $field=>$options)
        {
            if(isset($this->data[$field]))
            {
                if($options['type']=='array' && is_array($this->data[$field]))
                    $GLOBALS['redis']->hset($this->dbTable.':'.$this->id,$field,json_encode($this->data[$field]));
                else if($options['type']=='array' && !is_array($this->data[$field]))
                    $GLOBALS['redis']->hset($this->dbTable.':'.$this->id,$field,json_encode([]));
                else
                {
                    $GLOBALS['redis']->hset($this->dbTable.':'.$this->id,$field,$this->data[$field]);
                }
            }
                
        }

        return $this->id;
    }

    /**
     * @param $value The value to search for
     * @param string $field The field to search in. HAS to be marked as unique otherwise will throw error
     */
    public function load($value,$field='id')
    {
        if($field!='id')
        {
            //sanity check. Check if $field is marked as unique
            if(!in_array('unique',$this->dbFields[$field]))
                throw new Exception($field.' is not unique');
            //we need to find the id first
            $keys = $GLOBALS['redis']->keys($this->dbTable.':*');
            foreach($keys as $key){
                $id = end(explode(':',$key));
                $thisval = $GLOBALS['redis']->hget($this->dbTable.':'.$id,$field);
                if($thisval==$value)
                {
                    $this->id = $id;
                    break;
                }
            }
        }
        else
            $this->id = $value;
        if(!$GLOBALS['redis']->exists($this->dbTable.':'.$this->id))
            throw new Exception($this->dbTable.':'.$this->id.' not found');
        $keys = array_keys($this->dbFields);

        foreach($keys as $key)
        {
            $value = $GLOBALS['redis']->hget($this->dbTable.':'.$this->id,$key);

            if($value!==NULL) //we'll leave null values alone
                switch($this->dbFields[$key]['type'])
                {
                    case 'int': $value = intval($value);break;
                    case 'bool': $value = boolval($value);break;
                    case 'float': $value = floatval($value);break;
                    case 'double': $value = doubleval($value);break;
                    case 'array':
                        if(!$value)
                            $value = [];
                        else if(is_string($value))
                            $value = json_decode($value,true);
                    break;
                }
            $this->data[$key] = $value;
        }

        return true;
    }

    /**
     * @param array $data
     */
    private function validate($newgen=false)
    {
        if (!$this->dbFields)
            return true;

        $data = $this->data;

        foreach ($this->dbFields as $key => $options) {
            $type = null;
            $required = false;
            
            if(in_array('autoupdate',$options))
                $this->data[$key] = NULL;

            if (isset($data[$key]))
                $value = $data[$key];
            else
                $value = null;
            
            if (is_array($value))
                continue;

            // if (isset($desc[0]))
            //     $type = $desc[0];
            if (in_array('required',$options))
                $required = true;

            if($value===null && $options['autoValMethod'])
            {
                if(method_exists($this,$options['autoValMethod']))
                    $value = $this->{$options['autoValMethod']}();
                else if(function_exists($options['autoValMethod']))
                    $value = $options['autoValMethod']();
                else
                    $value = null;
                $this->data[$key] = $value;
            }

            if($options['default']!==null && $newgen === true && !$value)
            {
                $value = $options['default'];
                $this->data[$key] = $value;
            }

            if ($required && strlen($value) == 0) {
                throw new Exception($this->dbTable . "." . $key . " is required");
            }
            if ($value == null)
                continue;

            switch ($type) {
                case 'email':
                    $regexp = null;
                    if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                        throw new Exception("$type validation failed");
                    }
                break;
                case 'array':
                    if(!is_array($value))
                        throw new Exception('Dogs validation failed');
                break;
                case 'csv':
                case "text":
                    $regexp = null;
                    break;
                case "int":
                    $regexp = "/^[0-9]*$/";
                    break;
                case "double":
                    $regexp = "/^[0-9\.]*$/";
                    break;
                case "bool":
                    $regexp = '/^(yes|no|0|1|true|false)$/i';
                    break;
                case "datetime":
                    $regexp = "/^[0-9a-zA-Z -:]*$/";
                    break;
                default:
                    $regexp = $type;
                    break;
            }
            if (!$regexp)
                continue;

            if (!preg_match($regexp, $value)) {
                throw new Exception("$type validation failed");
            }
        }
        return true;
    }

    function exists($id)
    {
        return $GLOBALS['redis']->exists($this->dbTable.':'.$id);
    }

    function delete()
    {
        return $GLOBALS['redis']->del($this->dbTable.':'.$this->id);
    }

    function gen_shorthash(){
        return substr(uniqid(),-7);
    }

    function getDateTime($time=false)
    {
        return date('Y-m-d H:i:s',($time?$time:time()));
    }
}