<?php
namespace ProcessMaker\BusinessModel;

require_once(PATH_RBAC . "model" . PATH_SEP . "Roles.php");

class Role
{
    private $arrayFieldDefinition = array(
        "ROL_UID"    => array("type" => "string", "required" => false, "empty" => false, "defaultValues" => array(),                     "fieldNameAux" => "roleUid"),

        "ROL_CODE"   => array("type" => "string", "required" => true,  "empty" => false, "defaultValues" => array(),                     "fieldNameAux" => "roleCode"),
        "ROL_NAME"   => array("type" => "string", "required" => true,  "empty" => false, "defaultValues" => array(),                     "fieldNameAux" => "roleName"),
        "ROL_STATUS" => array("type" => "string", "required" => false, "empty" => false, "defaultValues" => array("ACTIVE", "INACTIVE"), "fieldNameAux" => "roleStatus")
    );

    private $formatFieldNameInUppercase = true;

    private $arrayFieldNameForException = array(
        "filter" => "FILTER",
        "start"  => "START",
        "limit"  => "LIMIT"
    );

    /**
     * Constructor of the class
     *
     * return void
     */
    public function __construct()
    {
        try {
            foreach ($this->arrayFieldDefinition as $key => $value) {
                $this->arrayFieldNameForException[$value["fieldNameAux"]] = $key;
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Set the format of the fields name (uppercase, lowercase)
     *
     * @param bool $flag Value that set the format
     *
     * return void
     */
    public function setFormatFieldNameInUppercase($flag)
    {
        try {
            $this->formatFieldNameInUppercase = $flag;

            $this->setArrayFieldNameForException($this->arrayFieldNameForException);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Set exception messages for fields
     *
     * @param array $arrayData Data with the fields
     *
     * return void
     */
    public function setArrayFieldNameForException(array $arrayData)
    {
        try {
            foreach ($arrayData as $key => $value) {
                $this->arrayFieldNameForException[$key] = $this->getFieldNameByFormatFieldName($value);
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get the name of the field according to the format
     *
     * @param string $fieldName Field name
     *
     * return string Return the field name according the format
     */
    public function getFieldNameByFormatFieldName($fieldName)
    {
        try {
            return ($this->formatFieldNameInUppercase)? strtoupper($fieldName) : strtolower($fieldName);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Verify if exists the code of a Role
     *
     * @param string $roleCode       Code
     * @param string $roleSystemUid  Unique id of System (00000000000000000000000000000001: RBAC, 00000000000000000000000000000002: PROCESSMAKER)
     * @param string $roleUidExclude Unique id of Role to exclude
     *
     * return bool Return true if exists the code of a Role, false otherwise
     */
    public function existsCode($roleCode, $roleSystemUid, $roleUidExclude = "")
    {
        try {
            $criteria = new \Criteria("rbac");

            $criteria->addSelectColumn(\RolesPeer::ROL_UID);

            $criteria->add(\RolesPeer::ROL_SYSTEM, $roleSystemUid, \Criteria::EQUAL);

            if ($roleUidExclude != "") {
                $criteria->add(\RolesPeer::ROL_UID, $roleUidExclude, \Criteria::NOT_EQUAL);
            }

            $criteria->add(\RolesPeer::ROL_CODE, $roleCode, \Criteria::EQUAL);

            $rsCriteria = \RolesPeer::doSelectRS($criteria);

            if ($rsCriteria->next()) {
                return true;
            } else {
                return false;
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Verify if exists the name of a Role
     *
     * @param string $roleName       Name
     * @param string $roleSystemUid  Unique id of System (00000000000000000000000000000001: RBAC, 00000000000000000000000000000002: PROCESSMAKER)
     * @param string $roleUidExclude Unique id of Role to exclude
     *
     * return bool Return true if exists the name of a Role, false otherwise
     */
    public function existsName($roleName, $roleSystemUid, $roleUidExclude = "")
    {
        try {
            //Set variables
            $content = new \Content();
            $role = new \Roles();

            $arrayContentByRole = $content->getAllContentsByRole();

            //SQL
            $criteria = new \Criteria("rbac");

            $criteria->addSelectColumn(\RolesPeer::ROL_UID);

            $criteria->add(\RolesPeer::ROL_SYSTEM, $roleSystemUid, \Criteria::EQUAL);

            if ($roleUidExclude != "") {
                $criteria->add(\RolesPeer::ROL_UID, $roleUidExclude, \Criteria::NOT_EQUAL);
            }

            $rsCriteria = \RolesPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $roleUid = $row["ROL_UID"];

                if (isset($arrayContentByRole[$roleUid])) {
                    $roleNameAux = $arrayContentByRole[$roleUid];
                } else {
                    $rowAux = $role->load($roleUid);
                    $roleNameAux = $rowAux["ROL_NAME"];
                }

                if ($roleNameAux == $roleName) {
                    return true;
                }
            }

            return false;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Verify if does not exist the Role in table ROLES
     *
     * @param string $roleUid               Unique id of Role
     * @param string $fieldNameForException Field name for the exception
     *
     * return void Throw exception if does not exist the Role in table ROLES
     */
    public function throwExceptionIfNotExistsRole($roleUid, $fieldNameForException)
    {
        try {
            $obj = \RolesPeer::retrieveByPK($roleUid);

            if (is_null($obj)) {
                throw new \Exception(\G::LoadTranslation("ID_ROLE_DOES_NOT_EXIST", array($fieldNameForException, $roleUid)));
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Verify if exists the code of a Role
     *
     * @param string $roleCode              Code
     * @param string $roleSystemUid         Unique id of System (00000000000000000000000000000001: RBAC, 00000000000000000000000000000002: PROCESSMAKER)
     * @param string $fieldNameForException Field name for the exception
     * @param string $roleUidExclude        Unique id of Role to exclude
     *
     * return void Throw exception if exists the code of a Role
     */
    public function throwExceptionIfExistsCode($roleCode, $roleSystemUid, $fieldNameForException, $roleUidExclude = "")
    {
        try {
            if ($this->existsCode($roleCode, $roleSystemUid, $roleUidExclude)) {
                throw new \Exception(\G::LoadTranslation("ID_ROLE_CODE_ALREADY_EXISTS", array($fieldNameForException, $roleCode)));
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Verify if exists the name of a Role
     *
     * @param string $roleName              Name
     * @param string $roleSystemUid         Unique id of System (00000000000000000000000000000001: RBAC, 00000000000000000000000000000002: PROCESSMAKER)
     * @param string $fieldNameForException Field name for the exception
     * @param string $roleUidExclude        Unique id of Role to exclude
     *
     * return void Throw exception if exists the name of a Role
     */
    public function throwExceptionIfExistsName($roleName, $roleSystemUid, $fieldNameForException, $roleUidExclude = "")
    {
        try {
            if ($this->existsName($roleName, $roleSystemUid, $roleUidExclude)) {
                throw new \Exception(\G::LoadTranslation("ID_ROLE_NAME_ALREADY_EXISTS", array($fieldNameForException, $roleName)));
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Validate the data if they are invalid (INSERT and UPDATE)
     *
     * @param string $roleUid   Unique id of Role
     * @param array  $arrayData Data
     *
     * return void Throw exception if data has an invalid value
     */
    public function throwExceptionIfDataIsInvalid($roleUid, array $arrayData)
    {
        try {
            //Set variables
            $arrayRoleData = ($roleUid == "")? array() : $this->getRole($roleUid, true);
            $flagInsert    = ($roleUid == "")? true : false;

            $arrayDataMain = array_merge($arrayRoleData, $arrayData);

            //Verify data - Field definition
            $process = new \ProcessMaker\BusinessModel\Process();

            $process->throwExceptionIfDataNotMetFieldDefinition($arrayData, $this->arrayFieldDefinition, $this->arrayFieldNameForException, $flagInsert);

            //Verify data
            if (isset($arrayData["ROL_CODE"]) && !preg_match("/^\w+$/", $arrayData["ROL_CODE"])) {
                throw new \Exception(\G::LoadTranslation("ID_ROLE_FIELD_CANNOT_CONTAIN_SPECIAL_CHARACTERS", array($this->arrayFieldNameForException["roleCode"])));
            }

            if (isset($arrayData["ROL_CODE"])) {
                $this->throwExceptionIfExistsCode($arrayData["ROL_CODE"], $arrayDataMain["ROL_SYSTEM"], $this->arrayFieldNameForException["roleCode"], $roleUid);
            }

            if (isset($arrayData["ROL_NAME"])) {
                $this->throwExceptionIfExistsName($arrayData["ROL_NAME"], $arrayDataMain["ROL_SYSTEM"], $this->arrayFieldNameForException["roleName"], $roleUid);
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Create Role
     *
     * @param array $arrayData Data
     *
     * return array Return data of the new Role created
     */
    public function create(array $arrayData)
    {
        try {
            //Verify data
            $process = new \ProcessMaker\BusinessModel\Process();
            $validator = new \ProcessMaker\BusinessModel\Validator();

            $validator->throwExceptionIfDataIsEmpty($arrayData, "\$arrayData");

            //Set data
            $arrayData = array_change_key_case($arrayData, CASE_UPPER);

            unset($arrayData["ROL_UID"]);

            $arrayData["ROL_SYSTEM"] = "00000000000000000000000000000002"; //PROCESSMAKER

            //Verify data
            $this->throwExceptionIfDataIsInvalid("", $arrayData);

            //Create
            $role = new \Roles();

            $roleUid = \ProcessMaker\Util\Common::generateUID();

            $arrayData["ROL_UID"]         = $roleUid;
            $arrayData["ROL_STATUS"]      = (isset($arrayData["ROL_STATUS"]))? (($arrayData["ROL_STATUS"] == "ACTIVE")? 1 : 0) : 1;
            $arrayData["ROL_CREATE_DATE"] = date("Y-M-d H:i:s");

            $result = $role->createRole($arrayData);

            //Return
            return $this->getRole($roleUid);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Update Role
     *
     * @param string $roleUid   Unique id of Role
     * @param array  $arrayData Data
     *
     * return array Return data of the Role updated
     */
    public function update($roleUid, array $arrayData)
    {
        try {
            //Verify data
            $process = new \ProcessMaker\BusinessModel\Process();
            $validator = new \ProcessMaker\BusinessModel\Validator();

            $validator->throwExceptionIfDataIsEmpty($arrayData, "\$arrayData");

            //Set data
            $arrayData = array_change_key_case($arrayData, CASE_UPPER);
            $arrayDataBackup = $arrayData;

            $arrayRoleData = $this->getRole($roleUid);

            //Verify data
            $this->throwExceptionIfNotExistsRole($roleUid, $this->arrayFieldNameForException["roleUid"]);

            if ($roleUid == "00000000000000000000000000000002") {
                throw new \Exception(\G::LoadTranslation("ID_ROLES_MSG"));
            }

            $this->throwExceptionIfDataIsInvalid($roleUid, $arrayData);

            //Update
            $role = new \Roles();

            $arrayData["ROL_UID"]         = $roleUid;
            $arrayData["ROL_UPDATE_DATE"] = date("Y-M-d H:i:s");

            if (!isset($arrayData["ROL_NAME"])) {
                $arrayData["ROL_NAME"] = $arrayRoleData[$this->getFieldNameByFormatFieldName("ROL_NAME")];
            }

            if (isset($arrayData["ROL_STATUS"])) {
                $arrayData["ROL_STATUS"] = ($arrayData["ROL_STATUS"] == "ACTIVE")? 1 : 0;
            }

            $result = $role->updateRole($arrayData);

            $arrayData = $arrayDataBackup;

            //Return
            if (!$this->formatFieldNameInUppercase) {
                $arrayData = array_change_key_case($arrayData, CASE_LOWER);
            }

            return $arrayData;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Delete Role
     *
     * @param string $roleUid Unique id of Role
     *
     * return void
     */
    public function delete($roleUid)
    {
        try {
            $role = new \Roles();

            //Verify data
            $this->throwExceptionIfNotExistsRole($roleUid, $this->arrayFieldNameForException["roleUid"]);

            if ($role->numUsersWithRole($roleUid) > 0) {
                throw new \Exception(\G::LoadTranslation("ID_ROLES_CAN_NOT_DELETE"));
            }

            //Delete
            $result = $role->removeRole($roleUid);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get criteria for Role
     *
     * return object
     */
    public function getRoleCriteria()
    {
        try {
            $criteria = new \Criteria("rbac");

            $criteria->addSelectColumn(\RolesPeer::ROL_UID);
            $criteria->addSelectColumn(\RolesPeer::ROL_PARENT);
            $criteria->addSelectColumn(\RolesPeer::ROL_CODE);
            $criteria->addSelectColumn(\RolesPeer::ROL_STATUS);
            $criteria->addSelectColumn(\RolesPeer::ROL_SYSTEM);
            $criteria->addSelectColumn(\RolesPeer::ROL_CREATE_DATE);
            $criteria->addSelectColumn(\RolesPeer::ROL_UPDATE_DATE);

            $criteria->add(\RolesPeer::ROL_SYSTEM, "00000000000000000000000000000002", \Criteria::EQUAL); //PROCESSMAKER

            return $criteria;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get data of a Role from a record
     *
     * @param array $record Record
     *
     * return array Return an array with data Role
     */
    public function getRoleDataFromRecord(array $record)
    {
        try {
            $conf = new \Configurations();
            $confEnvSetting = $conf->getFormats();

            $dateTime = new \DateTime($record["ROL_CREATE_DATE"]);
            $roleCreateDate = $dateTime->format($confEnvSetting["dateFormat"]);

            $roleUpdateDate = "";

            if (!empty($record["ROL_UPDATE_DATE"])) {
                $dateTime = new \DateTime($record["ROL_UPDATE_DATE"]);
                $roleUpdateDate = $dateTime->format($confEnvSetting["dateFormat"]);
            }

            return array(
                $this->getFieldNameByFormatFieldName("ROL_UID")         => $record["ROL_UID"],
                $this->getFieldNameByFormatFieldName("ROL_CODE")        => $record["ROL_CODE"],
                $this->getFieldNameByFormatFieldName("ROL_NAME")        => $record["ROL_NAME"],
                $this->getFieldNameByFormatFieldName("ROL_STATUS")      => ($record["ROL_STATUS"] . "" == "1")? "ACTIVE" : "INACTIVE",
                $this->getFieldNameByFormatFieldName("ROL_SYSTEM")      => $record["ROL_SYSTEM"],
                $this->getFieldNameByFormatFieldName("ROL_CREATE_DATE") => $roleCreateDate,
                $this->getFieldNameByFormatFieldName("ROL_UPDATE_DATE") => $roleUpdateDate,
                $this->getFieldNameByFormatFieldName("ROL_TOTAL_USERS") => (int)($record["ROL_TOTAL_USERS"])
            );
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get all Roles
     *
     * @param array  $arrayFilterData Data of the filters
     * @param string $sortField       Field name to sort
     * @param string $sortDir         Direction of sorting (ASC, DESC)
     * @param int    $start           Start
     * @param int    $limit           Limit
     *
     * return array Return an array with all Roles
     */
    public function getRoles(array $arrayFilterData = null, $sortField = null, $sortDir = null, $start = null, $limit = null)
    {
        try {
            $arrayRole = array();

            //Verify data
            $process = new \ProcessMaker\BusinessModel\Process();

            $process->throwExceptionIfDataNotMetPagerVarDefinition(array("start" => $start, "limit" => $limit), $this->arrayFieldNameForException);

            //Get data
            if (!is_null($limit) && $limit . "" == "0") {
                return $arrayRole;
            }

            //Set variables
            $content = new \Content();
            $role = new \Roles();

            $arrayContentByRole = $content->getAllContentsByRole();

            //SQL
            $criteria = $this->getRoleCriteria();

            $criteria->addAsColumn("ROL_TOTAL_USERS", "(SELECT COUNT(" . \UsersRolesPeer::ROL_UID . ") FROM " . \UsersRolesPeer::TABLE_NAME . " WHERE " . \UsersRolesPeer::ROL_UID . " = " . \RolesPeer::ROL_UID . ")");

            if (!is_null($arrayFilterData) && is_array($arrayFilterData) && isset($arrayFilterData["filter"]) && trim($arrayFilterData["filter"]) != "") {
                $criteria->add(\RolesPeer::ROL_CODE, "%" . $arrayFilterData["filter"] . "%", \Criteria::LIKE);
            }

            //SQL
            if (!is_null($sortField) && trim($sortField) != "") {
                $sortField = strtoupper($sortField);

                if (in_array($sortField, array("ROL_UID", "ROL_PARENT", "ROL_STATUS", "ROL_SYSTEM", "ROL_CREATE_DATE", "ROL_UPDATE_DATE"))) {
                    $sortField = \RolesPeer::TABLE_NAME . "." . $sortField;
                } else {
                    $sortField = \RolesPeer::ROL_CODE;
                }
            } else {
                $sortField = \RolesPeer::ROL_CODE;
            }

            if (!is_null($sortDir) && trim($sortDir) != "" && strtoupper($sortDir) == "DESC") {
                $criteria->addDescendingOrderByColumn($sortField);
            } else {
                $criteria->addAscendingOrderByColumn($sortField);
            }

            if (!is_null($start)) {
                $criteria->setOffset((int)($start));
            }

            if (!is_null($limit)) {
                $criteria->setLimit((int)($limit));
            }

            $rsCriteria = \RolesPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $row = $rsCriteria->getRow();

                $roleUid = $row["ROL_UID"];

                if (isset($arrayContentByRole[$roleUid])) {
                    $roleName = $arrayContentByRole[$roleUid];
                } else {
                    $rowAux = $role->load($roleUid);
                    $roleName = $rowAux["ROL_NAME"];
                }

                $row["ROL_NAME"] = $roleName;

                $arrayRole[] = $this->getRoleDataFromRecord($row);
            }

            //Return
            return $arrayRole;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Get data of a Role
     *
     * @param string $roleUid       Unique id of Role
     * @param bool   $flagGetRecord Value that set the getting
     *
     * return array Return an array with data of a Role
     */
    public function getRole($roleUid, $flagGetRecord = false)
    {
        try {
            //Verify data
            $this->throwExceptionIfNotExistsRole($roleUid, $this->arrayFieldNameForException["roleUid"]);

            //Set variables
            if (!$flagGetRecord) {
                $content = new \Content();
                $role = new \Roles();

                $arrayContentByRole = $content->getAllContentsByRole();
            }

            //Get data
            //SQL
            $criteria = $this->getRoleCriteria();

            if (!$flagGetRecord) {
                $criteria->addAsColumn("ROL_TOTAL_USERS", "(SELECT COUNT(" . \UsersRolesPeer::ROL_UID . ") FROM " . \UsersRolesPeer::TABLE_NAME . " WHERE " . \UsersRolesPeer::ROL_UID . " = " . \RolesPeer::ROL_UID . ")");
            }

            $criteria->add(\RolesPeer::ROL_UID, $roleUid, \Criteria::EQUAL);

            $rsCriteria = \RolesPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(\ResultSet::FETCHMODE_ASSOC);

            $rsCriteria->next();

            $row = $rsCriteria->getRow();

            if (!$flagGetRecord) {
                if (isset($arrayContentByRole[$roleUid])) {
                    $roleName = $arrayContentByRole[$roleUid];
                } else {
                    $rowAux = $role->load($roleUid);
                    $roleName = $rowAux["ROL_NAME"];
                }

                $row["ROL_NAME"] = $roleName;
            }

            //Return
            return (!$flagGetRecord)? $this->getRoleDataFromRecord($row) : $row;
        } catch (\Exception $e) {
            throw $e;
        }
    }
}

