<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 British Columbia Institute of Technology
 * Copyright (c) 2019-2020 CodeIgniter Foundation
 *
 * 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.
 *
 * @package    CodeIgniter
 * @author     CodeIgniter Dev Team
 * @copyright  2019-2020 CodeIgniter Foundation
 * @license    https://opensource.org/licenses/MIT    MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\RESTful;

use CodeIgniter\API\ResponseTrait;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;

/**
 * An extendable controller to provide a RESTful API for a resource.
 *
 * @package CodeIgniter\RESTful
 */
class ResourceController extends Controller
{

    use ResponseTrait;

    /**
     *
     * @var string Name of the model class managing this resource's data
     */
    protected $modelName;

    /**
     *
     * @var \CodeIgniter\Model the model holding this resource's data
     */
    protected $model;

    //--------------------------------------------------------------------

    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);

        // instantiate our model, if needed
        $this->setModel($this->modelName);
    }

    //--------------------------------------------------------------------

    /**
     * Return an array of resource objects, themselves in array format
     *
     * @return array    an array
     */
    public function index()
    {
        return $this->fail(lang('RESTful.notImplemented', ['index']), 501);
    }

    /**
     * Return the properties of a resource object
     *
     * @return array    an array
     */
    public function show($id = null)
    {
        return $this->fail(lang('RESTful.notImplemented', ['show']), 501);
    }

    /**
     * Return a new resource object, with default properties
     *
     * @return array    an array
     */
    function new () {
        return $this->fail(lang('RESTful.notImplemented', ['new']), 501);
    }

    /**
     * Create a new resource object, from "posted" parameters
     *
     * @return array    an array
     */
    public function create()
    {
        return $this->fail(lang('RESTful.notImplemented', ['create']), 501);
    }

    /**
     * Return the editable properties of a resource object
     *
     * @return array    an array
     */
    public function edit($id = null)
    {
        return $this->fail(lang('RESTful.notImplemented', ['edit']), 501);
    }

    /**
     * Add or update a model resource, from "posted" properties
     *
     * @return array    an array
     */
    public function update($id = null)
    {
        return $this->fail(lang('RESTful.notImplemented', ['update']), 501);
    }

    /**
     * Delete the designated resource object from the model
     *
     * @return array    an array
     */
    public function delete($id = null)
    {
        return $this->fail(lang('RESTful.notImplemented', ['delete']), 501);
    }

    //--------------------------------------------------------------------

    /**
     * Set or change the model this controller is bound to.
     * Given either the name or the object, determine the other.
     *
     * @param string|object $which
     */
    public function setModel($which = null)
    {
        // save what we have been given
        if (!empty($which)) {
            if (is_object($which)) {
                $this->model = $which;
            } else {
                $this->modelName = $which;
            }
        }

        // make a model object if needed
        if (empty($this->model) && !empty($this->modelName)) {
            if (class_exists($this->modelName)) {
                $this->model = model($this->modelName);
            }
        }

        // determine model name if needed
        if (empty($this->modelName) && !empty($this->model)) {
            $this->modelName = get_class($this->model);
        }
    }

    /**
     * Set/change the expected response representation for returned objects
     *
     * @param string $format
     */
    public function setFormat(string $format = 'json')
    {
        if (in_array($format, ['json', 'xml'])) {
            $this->format = $format;
        }
    }

    /**
     * Returns array with a key name changed
     *
     * @param array $row_list
     * @param string $oldKey
     * @param string $newKey
     */
    protected function renameKey($row_list, $oldKey, $newKey)
    {
        $json = str_replace('"' . $oldKey . '":', '"' . $newKey . '":', json_encode($row_list));
        return json_decode($json, true);
    }

    /**
     * Fix garbage on json string
     *
     * @param string $json
     */
    protected function jsonFixer(string $json)
    {
        $patterns = [];
        /** garbage removal */
        $patterns[0] = "/([\s:,\{}\[\]])\s*'([^:,\{}\[\]]*)'\s*([\s:,\{}\[\]])/"; //Find any character except colons, commas, curly and square brackets surrounded or not by spaces preceded and followed by spaces, colons, commas, curly or square brackets...
        $patterns[1] = '/([^\s:,\{}\[\]]*)\{([^\s:,\{}\[\]]*)/'; //Find any left curly brackets surrounded or not by one or more of any character except spaces, colons, commas, curly and square brackets...
        $patterns[2] = "/([^\s:,\{}\[\]]+)}/"; //Find any right curly brackets preceded by one or more of any character except spaces, colons, commas, curly and square brackets...
        $patterns[3] = "/(}),\s*/"; //JSON.parse() doesn't allow trailing commas
        /** reformatting */
        $patterns[4] = '/([^\s:,\{}\[\]]+\s*)*[^\s:,\{}\[\]]+/'; //Find or not one or more of any character except spaces, colons, commas, curly and square brackets followed by one or more of any character except spaces, colons, commas, curly and square brackets...
        $patterns[5] = '/["\']+([^"\':,\{}\[\]]*)["\']+/'; //Find one or more of quotation marks or/and apostrophes surrounding any character except colons, commas, curly and square brackets...
        $patterns[6] = '/(")([^\s:,\{}\[\]]+)(")(\s+([^\s:,\{}\[\]]+))/'; //Find or not one or more of any character except spaces, colons, commas, curly and square brackets surrounded by quotation marks followed by one or more spaces and  one or more of any character except spaces, colons, commas, curly and square brackets...
        $patterns[7] = "/(')([^\s:,\{}\[\]]+)(')(\s+([^\s:,\{}\[\]]+))/"; //Find or not one or more of any character except spaces, colons, commas, curly and square brackets surrounded by apostrophes followed by one or more spaces and  one or more of any character except spaces, colons, commas, curly and square brackets...
        $patterns[8] = '/(})(")/'; //Find any right curly brackets followed by quotation marks...
        $patterns[9] = '/,\s+(})/'; //Find any comma followed by one or more spaces and a right curly bracket...
        $patterns[10] = '/\s+/'; //Find one or more spaces...
        $patterns[11] = '/^\s+/'; //Find one or more spaces at start of string...

        $replacements = [];
        /** garbage removal */
        $replacements[0] = '$1 "$2" $3'; //...and put quotation marks surrounded by spaces between them;
        $replacements[1] = '$1 { $2'; //...and put spaces between them;
        $replacements[2] = '$1 }'; //...and put a space between them;
        $replacements[3] = '$1'; //...so, remove trailing commas of any right curly brackets;
        /** reformatting */
        $replacements[4] = '"$0"'; //...and put quotation marks surrounding them;
        $replacements[5] = '"$1"'; //...and replace by single quotation marks;
        $replacements[6] = '\\$1$2\\$3$4'; //...and add back slashes to its quotation marks;
        $replacements[7] = '\\$1$2\\$3$4'; //...and add back slashes to its apostrophes;
        $replacements[8] = '$1, $2'; //...and put a comma followed by a space character between them;
        $replacements[9] = ' $1'; //...and replace by a space followed by a right curly bracket;
        $replacements[10] = ' '; //...and replace by one space;
        $replacements[11] = ''; //...and remove it.

        $result = preg_replace($patterns, $replacements, $json);

        return $result;
    }

    /**
     * Returns array list of all rows in model table,
     * $rename is an array of $oldKey=>$newKey. Param $where to use an optional where condition.
     * @param array $rename
     * @param array|null $where
     */
    public function getAllRows($rename, $where = null, $where_or = null)
    {

        $rows_list = $this->model->getAllRows($where, $where_or);

        if (empty($rows_list)) {
            $rows_list = [];
        } else {
            if ($rename != []) {
                foreach ($rename as $oldkey => $newKey) {
                    //do something with your $key and $value;
                    $rows_list = $this->renameKey($rows_list, $oldkey, $newKey);
                }
            }

        }

        return $rows_list;
    }

    /**
     * Returns array list of all rows in model table given a join condition with another table,
     * $rename is an array of $oldKey=>$newKey. Param $where to use an optional where condition.
     * @param array $rename
     * @param array|string|null $where
     * @param array|string|null $where_or
     * @param array|string|null $join_condition
     * @param string|null $join_table
     * @param string $join_type
     *
     */
    public function getJoinRows($rename, $where = null, $where_or = null,
        $join_table = null, $join_condition = null, $join_type = 'inner') {

        $rows_list = $this->model->getJoinRows($where, $where_or, $join_table,
            $join_condition, $join_type);

        if (empty($rows_list)) {
            $rows_list = [];
        } else {
            if ($rename != []) {
                foreach ($rename as $oldkey => $newKey) {
                    //do something with your $key and $value;
                    $rows_list = $this->renameKey($rows_list, $oldkey, $newKey);
                }
            }

        }

        return $rows_list;
    }

    /**
     * Returns array list of updated rows in model table, based on date last_sync
     * $rename is an array of $oldKey=>$newKey. Param $where to use an optional where condition.
     * @param array|null $rename
     * @param array|null $where
     */
    public function getUpdatedRows($rename, $where = null, $where_or = null, $timeStampColumn = 'last_update')
    {

        // $mysqltime = date ('Y-m-d H:i:s', $this->request->getVar('last_sync'));
        $rows_list = $this->model->getUpdatedRows($this->request->getVar('last_sync'), $where, $where_or, $timeStampColumn);
        // echo $rows->last_query();

        if (empty($rows_list)) {
            $rows_list = [];
        } else {
            if ($rename != []) {
                foreach ($rename as $oldkey => $newKey) {
                    $rows_list = $this->renameKey($rows_list, $oldkey, $newKey);
                }
            }
        }

        return $rows_list;
    }

    /**
     * Returns array list of all rows in model table based on $where
     * conditions
     * @param array $rename
     * @param string $where
     *
     * @return array|null
     */
    public function getAllSelectedRows($rename, $where)
    {

        // $mysqltime = date ('Y-m-d H:i:s', $this->request->getVar('last_sync'));
        $rows_list = $this->model->getSelectedRows($where);
        // echo $rows->last_query();

        if (empty($rows_list)) {
            $rows_list = [];
        } else {
            if ($rename != []) {
                foreach ($rename as $oldkey => $newKey) {
                    $rows_list = $this->renameKey($rows_list, $oldkey, $newKey);
                }
            }
        }

        return $rows_list;
    }

    /**
     * Returns datetime of db server
     *
     */
    protected function getServerDateTime()
    {

        $datetime = $this->model->query('SELECT now()')->getResultArray();
        return array_values($datetime)[0]['now()'];
    }

    /**
     * Send array $data to an endpoint $url with POST, with the same content-type as a html form
     * @param string $url
     * @param array $data
     */
    public function httpPostXform($url, $data)
    {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLINFO_HEADER_OUT, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }
    /**
     * Send array $data to an endpoint $url with GET, with the same content-type as a html form
     * @param string $url
     * @param array $data
     */
    public function httpGETXform($url, $data)
    {
        $curl = curl_init($url . '?' . http_build_query($data));
        // echo($url.'?'.http_build_query($data));
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
        // echo $curl;
        $response = curl_exec($curl);
        curl_close($curl);
        return $response;
    }

    public function dbGroupFromRequest(){
        $authHeader = $this->request->getServer('HTTP_AUTHORIZATION');
        helper('jwt');
        $token = getJWTFromRequest($authHeader);
        $dbGroup = dbGroupFromJWT($token);
        return $dbGroup;
    }
}