<?php
class DeveloperModelClass extends ModelClassEx {

    private $devKey                    = NO_STRING;
    private $passcode                  = NO_STRING;
    private $confirmPasscode           = NO_STRING;
    private $expirationDate            = NO_STRING;
    private $ipAddresses               = NO_STRING;
    
	private $lang		               = NO_STRING;
	private $result                    = [];
	
	// search fields
	private $sDevKey                   = NO_STRING;
	private $sExecutionCounter         = NO_STRING;
	
	// date-ranges
	private $expireDateSearch          = false; // flag
	private $expireDateFrom            = NO_STRING;
	private $expireDateTo              = NO_STRING;
	
	private $executionDateSearch       = false; // flag
	private $executionDateFrom         = NO_STRING;
	private $executionDateTo           = NO_STRING;
	
	private $createDateSearch          = false; // flag
	private $createDateFrom            = NO_STRING;
	private $createDateTo              = NO_STRING;
	
	private $updateDateSearch          = false; // flag
	private $updateDateFrom            = NO_STRING;
	private $updateDateTo              = NO_STRING;
	// end of date-ranges
	
	private $total                     = NO_COUNT;
	
	public function __construct() {
		parent::__construct();
	}


	/*-------------------------------------------------------------------------
	* @function_name: 
	* @parameter	: 
	* @return		: bool
	-------------------------------------------------------------------------*/
	public function init() {

		try {
			// パラメータ　設定
			$this -> setParameter();
			$this -> checkAccessPoints();

			return $this -> validate();
		} catch (Exception $e) {
			throw new Exception(NO_STRING);
		}
	}
	
	/*-------------------------------------------------------------------------
	* @function_name: パラメータ　設定
	* @parameter	: なし
	* @return		: 
	-------------------------------------------------------------------------*/
	private function setParameter() {
	    $this -> devKey            = $this -> getDataPost("developer_key");
	    $this -> passcode          = $this -> getDataPost("passcode");
	    $this -> confirmPasscode   = $this -> getDataPost("confirm_passcode");
	    $this -> expirationDate    = $this -> getDataPost("expiration_date");
	    $this -> ipAddresses       = $this -> getDataPost("ipAddresses");
	    
	    $this -> sDevKey           = $this -> getDataPost("sDevKey");
	    $this -> sExecutionCounter = $this -> getDataPost("sExecutionCounter");
	    
	    // Checkboxes
	    $this -> expireDateSearch      = $this -> getDataPost("expireDateSearch");
	    $this -> executionDateSearch   = $this -> getDataPost("executionDateSearch");
	    $this -> createDateSearch      = $this -> getDataPost("createDateSearch");
	    $this -> updateDateSearch      = $this -> getDataPost("updateDateSearch");
	    
	    // datepickes
	    $this -> expireDateFrom    = $this -> getDataPost("expireDateFrom");
	    $this -> expireDateTo      = $this -> getDataPost("expireDateTo");
	    $this -> executionDateFrom = $this -> getDataPost("executionDateFrom");
	    $this -> executionDateTo   = $this -> getDataPost("executionDateTo");
	    $this -> createDateFrom    = $this -> getDataPost("createDateFrom");
	    $this -> createDateTo      = $this -> getDataPost("createDateTo");
	    $this -> updateDateFrom    = $this -> getDataPost("updateDateFrom");
	    $this -> updateDateTo      = $this -> getDataPost("updateDateTo");
	    
		$this -> lang		       = $this -> getLangage();
		
		if(!$this -> checkNull($this -> expirationDate)){
		    $this -> expirationDate = date("Y/m/d", strtotime('+30 days',strtotime(date("Y-m-d"))));
		}
		
		$this -> datePickerDefaultValue($this -> expireDateFrom, "from");
		$this -> datePickerDefaultValue($this -> expireDateTo, "to");
		$this -> datePickerDefaultValue($this -> executionDateFrom, "from");
		$this -> datePickerDefaultValue($this -> executionDateTo, "to");
		$this -> datePickerDefaultValue($this -> createDateFrom, "from");
		$this -> datePickerDefaultValue($this -> createDateTo, "to");
		$this -> datePickerDefaultValue($this -> updateDateFrom, "from");
		$this -> datePickerDefaultValue($this -> updateDateTo, "to");
	}
	
	private function datePickerDefaultValue(&$dateRange, $direction){
	    if(!$this -> checkNull($dateRange)){
	        $dateRange = strcasecmp($direction, "from") == 0 ? date("Y/m/01") : 
	           (strcasecmp($direction, "to") == 0 ? date("Y/m/t") : NO_STRING);
	    }
	}
	
	private function checkAccessPoints(){
	    $redirect = ($this -> lang != "en" ? "" : "{$this -> lang}/");
	    
	    //check access
	    if(!$this -> checkAdminUserAccess('DEVF', array(''))){
	        $this -> popUpSessionMessage(ERROR, 'E_ERROR_NO_PAGE_ACCESS', array());
	        header('Location: /'.$redirect.'menu');
	        exit();
	    }
	    
	    switch($this -> getType()){
	        case TYPE_DEV_CREDENTIAL_EDIT:
	        case TYPE_DEV_CREDENTIAL_MODIFY_PASSCODE:
	        case TYPE_DEV_CREDENTIAL_PASSCODE_UPDATE:
	        case TYPE_DEV_CREDENTIAL_UPDATE:
	            if(!$this -> checkAdminUserAccess('DEVF', array('','1'))){
	                $this -> popUpSessionMessage(ERROR, 'E_ERROR_NO_PAGE_ACCESS', array());
	                header('Location: /'.$redirect.'menu');
	                exit();
	            }
	            break;
	        case TYPE_DEV_CREDENTIAL_DELETE:{
	            if(!$this -> checkAdminUserAccess('DEVF', array('','2'))){
	                $this -> popUpSessionMessage(ERROR, 'E_ERROR_NO_PAGE_ACCESS', array());
	                header('Location: /'.$redirect.'menu');
	                exit();
	            }
	            break;
	        }
	    }
	}
	
	public function validate() {
		$rtn = true;
		
		if($this -> getType() == TYPE_NEW_DEV_CREDENTIAL)
		    $this -> resetSession("dev_cred_passcode");
		
		if($this -> getType() == TYPE_DEV_CREDENTIAL_DELETE)
		    $this -> expirationDate = NO_STRING;
		
		if($this -> getType() == TYPE_DEV_CREDENTIAL_MODIFY_PASSCODE){
		    $this -> resetSession("edit_dev_key");
		    $_SESSION["edit_dev_key"] = $this -> devKey;
		}
		
		
		if($this -> getType() == TYPE_DEV_CREDENTIAL_PASSCODE_UPDATE){
		    $this -> expirationDate = NO_STRING;
		    
		    $conditions = [
		        !$this -> validateDevKeyModId(),
		        !$this -> validatePasscode()
		    ];
		    
		    if($this -> errorCounter($conditions) != NO_STRING){
		        $this -> setType(TYPE_DEV_CREDENTIAL_MODIFY_PASSCODE);
		        $rtn = false;
		    }else
		        $this -> resetSession("edit_dev_key");
		}
		
		if($this -> getType() == TYPE_DEV_CREDENTIAL_EDIT){   
		    $this -> resetSession("edit_dev_key");
		    $_SESSION["edit_dev_key"] = $this -> devKey;
		    $this -> restore();
		}
		
		if($this -> getType() == TYPE_DEV_CREDENTIAL_UPDATE){
		    $conditions = [
		        !$this -> validateDevKeyModId(),
		    ];
		    
		    if($this -> errorCounter($conditions) != NO_COUNT){
		        $this -> setType(TYPE_DEV_CREDENTIAL_EDIT);
		        $rtn = false;
		    }else
		        $this -> resetSession("edit_dev_key");
		}
		
		if($this -> getType() == TYPE_NEW_DEV_CREDENTIAL_CONFIRM){	
		    $conditions = $this -> getConditions();
		    
		    if($this -> errorCounter($conditions) != NO_COUNT){
		        $this -> setType(TYPE_NEW_DEV_CREDENTIAL);
		        $rtn = false;
		    }else{
		        $this -> resetSession("dev_cred_passcode");
		        $_SESSION["dev_cred_passcode"] = $this -> passcode;
		    }
		}
		
		if($this -> getType() == TYPE_NEW_DEV_CREDENTIAL_COMPLETE){
		    $conditions = $this -> getConditions();
		    $conditions[4] = !$this -> validatePasscode(true);
		    
		    if($this -> errorCounter($conditions) != NO_COUNT){
		        $this -> setType(TYPE_NEW_DEV_CREDENTIAL);
		        $rtn = false;
		    }else
		        $this -> resetSession("dev_cred_passcode");
		}
		
		return $rtn;
	}
	
	private function getConditions(){
	    return [
	        !$this -> validateRequiredValues(),
	        !$this -> validateCredential(),
	        !$this -> validatePasscode(),
	        !$this -> validateExpirationDate()
	    ];
	}
	
	private function errorCounter(&$conditions){
	    $err = NO_COUNT;
	    
	    foreach($conditions as $condition){
	        if($condition)
	            $err++;
	    }
	    
	    return $err;
	}
	
	private function getDevCredential($devKey){
	    return $this -> getRowData($this -> accessSelect("SELECT_DEV_CREDENTIAL", [$devKey]));
	}
	
	private function restore(){
	    $devCred = $this -> getDevCredential($this -> devKey);
	    
	    $this -> ipAddresses = $devCred["dev_ip_addresses"];
	    $this -> expirationDate = date("Y/m/d", strtotime($devCred["dev_expiration"]));
	}
	
	private function resetSession($sessionName){
	    if(isset($_SESSION[$sessionName]))
	        unset($_SESSION[$sessionName]);
	}
	
	private function validateRequiredValues(){
	    $rtn = true;
	    
	    if(!$this -> checkNull($this -> devKey)){
	        $this -> popUpSessionMessage(ERROR, 'E_REQUIRED_VALUE', [VAL_STR_DEV_KEY]);
	        $rtn = false;
	    }
	    
	    if(!$this -> checkNull($this -> passcode)){
	        $this -> popUpSessionMessage(ERROR, 'E_REQUIRED_VALUE', [VAL_STR_PASSCODE]);
	        $rtn = false;
	    }
	    
	    if(!$this -> checkNull($this -> confirmPasscode)){
	        $this -> popUpSessionMessage(ERROR, 'E_REQUIRED_VALUE', [VAL_STR_CONFIRM_PASSCODE]);
	        $rtn = false;
	    }
	    
	    if(!$this -> checkNull($this -> expirationDate)){
	        $this -> popUpSessionMessage(ERROR, 'E_REQUIRED_VALUE', [VAL_STR_EXPIRATION_DATE]);
	        $rtn = false;
	    }
	    
	    return $rtn;
	}
	
	private function validateDevKeyModId(){
	    if(isset($_SESSION["edit_dev_key"])){
	        if($this -> devKey != $_SESSION["edit_dev_key"]){
	            $this -> popUpSessionMessage(ERROR, 'E_ERROR_CONFIRM', [VAL_STR_DEV_KEY]);
	            return false;
	        }
	    }
	    
	    return true;
	}
	
	private function validateCredential(){
	    $cred = $this -> accessSelect("SELECT_DEV_CREDENTIAL", [$this -> devKey]);
	    
	    if($this -> isLoopData($cred)){
	       $this -> popUpSessionMessage(ERROR, 'E_EXIST_DATA', [VAL_STR_DEV_CREDENTIALS]);
	       return false; 
	    }
	    
	    return true;
	}
	
	private function validatePasscode($validateUsingSession = false){
	    if($validateUsingSession){
	        if(isset($_SESSION["dev_cred_passcode"])){
	            if($this -> passcode != $_SESSION["dev_cred_passcode"]){
	                $this -> popUpSessionMessage(ERROR, 'E_DEVELOPER_TOOLS_NOT_ALLOWED', []);
	                return false;
	            }
	        }
	    }else{
	        if($this -> passcode != $this -> confirmPasscode){
	            $this -> popUpSessionMessage(ERROR, 'E_ERROR_CONFIRM', [VAL_STR_PASSCODE]);
	            return false;
	        }
	    }   
	    return true;
	}
	
	private function validateExpirationDate(){
	    $currentDate = strtotime(date("Y-m-d"));
	    $toValidate = strtotime($this -> expirationDate);
	    
	    if($toValidate <= $currentDate){
	        $this -> popUpSessionMessage(ERROR, 'E_INVALID_DATE', []);
	        return false;   
	    }
	    
	    return true;
	}
	
	private function generateRandomString($length = 10) {
	    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	    $charactersLength = strlen($characters);
	    $randomString = '';
	    for ($i = 0; $i < $length; $i++) {
	        $randomString .= $characters[rand(0, $charactersLength - 1)];
	    }
	    return $randomString;
	}
	
	/**
	 * this is the place where we generate secret key for JWT payload
	 * 
	 * @return string[]
	 */
	public function getDevCredentialsData(){
	    $randstr = $this -> generateRandomString();
	    $secretKey = "devT{$randstr}";
	    
	    $securedPasscode = password_hash(
	        $this -> passcode.VAL_STR_AUTH_HASH_SECRET_KEY,
	        PASSWORD_BCRYPT,
	        array('cost' => 13)
	     );
	    
	    return [
	        $this -> devKey,
	        $securedPasscode,
	        NO_STRING,
	        date("Y-m-d H:i:s", strtotime($this -> expirationDate)),
	        $secretKey,
	        NO_COUNT,
	        "0000-00-00 00:00:00",
	        date("Y-m-d H:i:s"),
	        "0000-00-00 00:00:00",
	    ];
	}
	
	public function getUpdatedDevData(){
	    return [
	        $this -> devKey,
	        $this -> expirationDate,
	        trim($this -> ipAddresses),
	        date("Y-m-d H:i:s")
	    ];
	}
	
	public function getUpdatedDevCredPasscode(){
	    $securedPasscode = password_hash(
	        $this -> passcode.VAL_STR_AUTH_HASH_SECRET_KEY,
	        PASSWORD_BCRYPT,
	        array('cost' => 13)
	        );
	    
	    return [
	        $this -> devKey,
	        $securedPasscode,
	        date("Y-m-d H:i:s")
	    ];
	}
	
	public function echoList(){
	    $rows = NO_STRING;
	    
	    if($this -> isLoopData($this -> result)){
	        foreach($this -> result as $row){
	            
	            $expTime = strtotime($row["dev_expiration"]);
	            $curTime = strtotime(date("Y-m-d"));
	            $style = NO_STRING;
	            
	            if($expTime < $curTime)
	                $style = "style=\"color:#741506;font-weight:bold;\"";
	            
	            $rows .=<<<HTMLROW
               <tr>
                   <td><a class="dev-key-view" href="#" hold="{$row["dev_key"]}">{$row["dev_key"]}</a></td>
                   <td>{$row["dev_secret_key"]}</td>
                   <td {$style}>{$row["dev_expiration"]}</td>
                   <td>{$row["dev_exec_counter"]}</td>
                   <td>{$row["dev_last_exec"]}</td>
                   <td>{$row["create_time"]}</td>
                   <td>{$row["update_time"]}</td>
                   <td><a href="#" class="rmv-dev-cred" hold="{$row["dev_key"]}"><span class="fa fa-trash"></span></a></td> 
               </tr>
HTMLROW;
	        }   
	    }else
	        $rows = '<tr><td colspan="8">'.VAL_STR_SEARCH_MESSAGE.'</td></tr>';
	    
	    echo $rows;
	}
	
	public function getWhere(){
	    $build = NO_STRING;
	    
	    if($this -> checkNull($this -> sDevKey)){
	        $build .= " AND dev_key = ('){$this -> sDevKey}(')";
	    }
	    
	    if($this -> checkNull($this -> sExecutionCounter)){
	        $build .= " AND dev_exec_counter = ('){$this -> sExecutionCounter}(')";
	    }

	    if($this -> expireDateSearch){
	        if($this -> checkNull($this -> expireDateFrom)){
	            $build .= " AND cast(dev_expiration as date) >= ('){$this -> expireDateFrom}(')";
	        }
	        
	        if($this -> checkNull($this -> expireDateTo)){
	            $build .= " AND cast(dev_expiration as date) <= ('){$this -> expireDateTo}(')";
	        }
	    }
	    
	    if($this -> executionDateSearch){
	        if($this -> checkNull($this -> executionDateFrom)){
	            $build .= " AND cast(dev_last_exec as date) >= ('){$this -> executionDateFrom}(')";
	        }
	        
	        if($this -> checkNull($this -> executionDateTo)){
	            $build .= " AND cast(dev_last_exec as date) <= ('){$this -> executionDateTo}(')";
	        }
	    }
	    
	    if($this -> createDateSearch){
	        
	        if($this -> checkNull($this -> createDateFrom)){
	            $build .= " AND cast(create_time as date) >= ('){$this -> createDateFrom}(')";
	        }
	        
	        if($this -> checkNull($this -> createDateTo)){
	            $build .= " AND cast(create_time as date) <= ('){$this -> createDateTo}(')";
	        }
	    }
	    
	    if($this -> updateDateSearch){
	        if($this -> checkNull($this -> updateDateFrom)){
	            $build .= " AND cast(update_time as date) >= ('){$this -> updateDateFrom}(')";
	        }
	        
	        if($this -> checkNull($this -> updateDateTo)){
	            $build .= " AND cast(update_time as date) <= ('){$this -> updateDateTo}(')";
	        }
	    }
	    
	    return $build;
	}
	
	public function dispPager() {
	    echo  $this -> getPagerCommon($this -> getTargetPage()
	        , $this -> getTotalPageCommon(VAR_DEFAULT_PAGE_COUNT, $this -> total)
	        , $this -> total
	        , NO_STRING
	        , false);
	}
	
	public function dispDateRangeFlg($dateRangeFlgField) {
	    echo '<label>'
	        . $this -> makeCheckOne(
	            $dateRangeFlgField,
	            $dateRangeFlgField,
	            VAL_INT_1,
	            NO_STRING, 
	            $this -> {$dateRangeFlgField})
	     . '</label>';
	}
	
	public function setResult($result){
	    $this -> result = $result;
	}
	
	public function setTotal($total){
	    $this -> total = $total;
	}
	
	public function getFieldValue($fieldName){
	    return $this->{$fieldName};
	}
	
	public function getActivity(){
	    $detail = [];
	   
	    $cred = $this -> getDevCredential($this -> devKey);
	    
	    if($this -> isLoopData($cred)){
	        if($this -> getType() != TYPE_DEV_CREDENTIAL_DELETE){
	           if($this -> checkNull($this -> ipAddresses) && $cred["dev_ip_addresses"] != $this -> ipAddresses)
	                $detail[] = "Updated IP Address From [{$cred["dev_ip_addresses"]}] to [{$this -> ipAddresses}]";
	                
	           if($this -> checkNull($this -> expirationDate)){
	               $cred["dev_expiration"] = date("Y/m/d", strtotime($cred["dev_expiration"]));
	               
	               if($cred["dev_expiration"] != $this -> expirationDate){
	                   $detail[] = "Updated Expiration Date From [{$cred["dev_expiration"]}] to [{$this -> expirationDate}]";
	               }
	           }
	           
	           if($this -> checkNull($this -> passcode)){
	               if(!password_verify($this -> passcode.VAL_STR_AUTH_HASH_SECRET_KEY, $cred["dev_passcode"]))
	                   $detail[] = "Developer's Credential Passcode Updated [{$this -> devKey}]";
	           }
	        }else{
	            if($this -> checkNull($this -> devKey))
	                $detail[] = "Developer Credential Deleted [{$this -> devKey}]";
	        }
	    }else{
	        $detail[] = "New Developer Credential Added [{$this -> devKey}]";
	    }
	    
	    $mixed = implode(', <br/>', $detail);
	    
	    if($detail != NULL)
	        $message = "[Developers Option] <br/> <b>Update</b>: {$mixed}";
	    else
	        $message = "[Developers Option] Saved without any changes";
	    
	    $rtn = [
	        $this -> getAdminUserData(PARAM_UID),
	        $_SERVER[PARAM_REMOTE_ADDR],
	        $_SERVER[PARAM_HTTP_USER_AGENT],
	        VAL_INT_23,
	        date("Y-m-d H:i:s"),
	        $message
	    ];
	   	    
	    return $rtn;
	}
	
	public function dispHTML() {
        return array();
    }
}

