<?php
class sysDb{
	private $db;
	public $error;
	public function __construct( ){
		//DBファイルがない場合･･･
		if ( ! file_exists( CC\FILE::SYSDB )) {
			$this->error = CC\RES::RES_SYSERRORS[ 'SYS_F_NOT_FOUND' ];
			errLog(
				'SYSDB FILE NOT FOUND' ."\t".
				'DB PATH:'.CC\FILE::SYSDB."\r\n"
			);
			return $this;
		};
		$this->db = new dbAccess( CC\FILE::SYSDB );
		//DBファイルが有ったが、DBファイルに接続できない場合･･･
		if ( ! $this->db->isEnable() ) {
			$this->error = CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
			errLog(
				'CANNOT CONNECT SYSDB' ."\t".
				'DB PATH:'.CC\FILE::SYSDB."\r\n"
			);
		};
	}
	public function isEnable(){
		return $this->db->isEnable();
	}
	private function getTableData( $CONST_KEY ){
		$strSql = CC\SYSDB::SQL_AND_COLS[ $CONST_KEY ]['SQL'];
		$cols = CC\SYSDB::SQL_AND_COLS[ $CONST_KEY ]['COLS'];
		$types = CC\SYSDB::SQL_AND_COLS[ $CONST_KEY ]['TYPE'];
		$rslt = $this->db->query( $strSql );
		if( $rslt[ 'error'] != null ){
			errLog(
				'GET TABLE QUERY ERROR' ."\t".
				'SQL:'.$strSql."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};
		$ret = CC\RES::RES_I;
		$ret[ 'head' ] = $cols;
		$ret[ 'table' ] =[];
		foreach ($rslt["rows"] as $row) {

			$rowAry = [];
			foreach ($cols as $idx => $col) {
				if( array_key_exists( $col , $row ) ){
					$value = $row[ $col ];
					if( $types[$idx] ){
						if( $value == '' || $value == null ){
							$value = null;
						}else{
							$value = (float)$value;
						};
					}
					array_push( $rowAry , $value );
				}else{
					array_push( $rowAry , null );
					errLog(
						'SYSDB COLUMN NOT FOUND' ."\t".
						'COLUMN:'.$col."\r\n"
					);
					return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
				}
			};
			array_push( $ret[ 'table' ] , $rowAry );
		};
		return $ret;
	}
	public function getMstType(){
		return $this->getTableData('MST_TYPE');
	}
	public function getMstMode(){
		return $this->getTableData('MST_MODE');
	}
	public function getMstTypeMode(){
		return $this->getTableData('MST_TYPE_MODE');
	}

	public function getTblDb(){
		return $this->getTableData('TBL_DB');
	}
	public function getDbInfo( $id ){
		$strSql = CC\SYSDB::SQL_AND_COLS[ 'GET_DB_INFO' ]['SQL'];
		$cols = CC\SYSDB::SQL_AND_COLS[ 'GET_DB_INFO' ]['COLS'];
		$types = CC\SYSDB::SQL_AND_COLS[ 'GET_DB_INFO' ]['TYPE'];
		$sqlparam = [1=>['value' => $id ,'type' => PDO::PARAM_INT ]];

		$rslt = $this->db->select( $strSql , $sqlparam );
		if( $rslt[ 'error'] != null ){
			errLog(
				'GET TABLE QUERY ERROR' ."\t".
				'SQL:'.$strSql."\r\n"
			);
			return null;
		};
		if( count( $rslt['rows'] ) == 1){
			foreach ($rslt['rows'] as $row) {
				return $row;
			};
		};
		return null;

	}
	public function chgTblDb( $aryParam ){

		//まずは、全データを取得する。
		$current = $this->getTblDb();
		if( $current[ 'mode' ] != 'I' ){
			return $current;
		};
		$currentIds = [];
		$idKey = -1;
		foreach( $current[ 'head' ] as $key => $col ){
			if( $col == 'id' ){
				$idKey = $key;
				break;
			}
		}
		//レコードの存在の有無のみが問題であるので、IDをキー、値とした配列に放り込む
		foreach( $current[ 'table' ] as $row){
			$currentIds[ $row[ $idKey ] ] = $row[ $idKey ];
		}

		$rows = [ 'del'=>[] ,'upd'=>[] , 'ins'=>[] , ];
		$updIds = [];
		foreach( $aryParam as $param ){
			$cols = [];
			$sqlData;
			$targAry;
			if( $param['id'] === null ){
				$sqlData = CC\SYSDB::SQL_AND_COLS['INS_TBL_DB'];
				$targAry = &$rows['ins'];
			}else{
				if( array_key_exists( $param['id'] , $currentIds ) ){
					$sqlData = CC\SYSDB::SQL_AND_COLS['UPD_TBL_DB'];
					$targAry = &$rows['upd'];
					$updIds[ $param['id'] ] = $param['id'];
				}else{
					$sqlData = CC\SYSDB::SQL_AND_COLS['INS_TBL_DB'];
					$targAry = &$rows['ins'];
				}
			}
			foreach( $sqlData[ 'COLS' ] as $idx => $col ){
				$sqlparam = ['value' => $param[ $col ] ,'type' => PDO::PARAM_STR ];
				if( $sqlData[ 'TYPE' ][ $idx ] ){
					$sqlparam[ 'type' ] = PDO::PARAM_INT;
				};
				$cols[ $idx + 1 ] = $sqlparam;
			}
			$targAry[] = $cols;
		};
		//差分を取得し、削除するIDを取得する。
		$deleteIds = array_diff($currentIds, $updIds);
		$targAry = &$rows['del'];
		foreach( $deleteIds as $id ){
			$sqlData = CC\SYSDB::SQL_AND_COLS['DEL_TBL_DB'];
			$sqlparam = ['value' => $id ,'type' => PDO::PARAM_INT ];
			$cols = [ 1 => $sqlparam];
			$targAry[] = $cols;
		}
		//トランザクションの開始。
		$ret = $this->db->begin();
		if( $ret['error'] != null ){
			errLog(
				'CANNOT BEGIN TRANSACTION' ."\t".
				'Exception:'.$ret['error']->getMessage( )."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		}
		//まずは削除を行う。
		$sqlData = CC\SYSDB::SQL_AND_COLS['DEL_TBL_DB'];
		$rslt = $this->db->write( $sqlData['SQL'] , $rows['del'] );
		if( $rslt['error'] != null ){
			$this->db->rollback();
			$errMsgs = [];
			foreach($rslt['error'] as $key => $err){
				$errMsgs[$key] = $err->getMessage( );
			}
			errLog(
				'DB DELETE ERROR' ."\t".
				'Exception:'.json_encode( $errMsgs )."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};
		//続いて更新を行う。
		$sqlData = CC\SYSDB::SQL_AND_COLS['UPD_TBL_DB'];
		$rslt = $this->db->write( $sqlData['SQL'] , $rows['upd'] );
		if( $rslt['error'] != null ){
			$this->db->rollback();
			$errMsgs = [];
			foreach($rslt['error'] as $key => $err){
				$errMsgs[$key] = $err->getMessage( );
			}
			errLog(
				'DB UPDATE ERROR' ."\t".
				'Exception:'.json_encode( $errMsgs )."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};

		//続いて新規追加を行う。
		$sqlData = CC\SYSDB::SQL_AND_COLS['INS_TBL_DB'];
		$rslt = $this->db->write( $sqlData['SQL'] , $rows['ins'] );
		if( $rslt['error'] != null ){
			$this->db->rollback();
			$errMsgs = [];
			foreach($rslt['error'] as $key => $err){
				$errMsgs[$key] = $err->getMessage( );
			}
			errLog(
				'DB UPDATE ERROR' ."\t".
				'Exception:'.json_encode( $errMsgs )."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};

		//改めて、全データを取得する。
		$current = $this->getTblDb();
		if( $current[ 'mode' ] != 'I' ){
			return $current;
		};
		$newIds = [];
		$idKey = -1;
		foreach( $current[ 'head' ] as $key => $col ){
			if( $col == 'id' ){
				$idKey = $key;
				break;
			}
		}
		//レコードの存在の有無のみが問題であるので、IDをキー、値とした配列に放り込む
		foreach( $current[ 'table' ] as $row){
			$newIds[ $row[ $idKey ] ] = $row[ $idKey ];
		}

		//差分を取得し、新規追加したIDのみを取得する。
		$insertedId = array_diff($newIds, $currentIds);

		//ここからファイル操作
		//まずは、削除対象ファイルをEXPORTフォルダに移動する。
		$delInfos = [];
		foreach( $rows['del'] as $param ){
			$id = $param[1]['value'];
			$delInfo = ['src'=>C\getDBPath($id),'dist'=>C\getDelDBPath($id)];
			$delInfos[] = $delInfo;
		}
		$hasError = false;
		$errorKey = null;
		foreach( $delInfos as $key => $delInfo ){
			if (! file_exists( $delInfo['src'] )) {
				continue;
			}
			$hasError = ! rename( $delInfo['src'] , $delInfo['dist'] );
			if( $hasError ){
				errLog(
					'DB FILE CANNOT MOVE' ."\t".
					'src:'.$delInfo['src']."\t".
					'dist:'.$delInfo['dist']."\r\n"
				);
				break;
				$errorKey = $key;
			}
		}
		if( $hasError ){
			$this->db->rollback();
			//ひとまず戻そうとしてみる。
			//ただし、正常に動作すると保障できるものではない。
			//やってみる、と言う程度の処理になる。
			foreach( $delInfos as $key => $delInfo ){
				if( $key == $errorKey ){
					break;
				};
				if (! file_exists( $delInfo['dist'] )) {
					continue;
				}
				rename( $delInfo['dist'] , $delInfo['src'] );
			}
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};



		//続いて、新規追加ファイルを追加する。
		foreach( $insertedId as $key => $id ){
			$hasError = ! copy( CC\FILE::DBTPL , C\getDBPath($id) );
			if( $hasError ){
				errLog(
					'DB FILE CANNOT COPY' ."\t".
					'src:'.CC\FILE::DBTPL."\t".
					'dist:'.C\getDBPath($id)."\r\n"
				);
				break;
				$errorKey = $key;
			}
		}
		if( $hasError ){
			$this->db->rollback();
			//ひとまず戻そうとしてみる。
			//ただし、正常に動作すると保障できるものではない。
			//やってみる、と言う程度の処理になる。
			foreach( $insertedId as $key => $id ){
				if( $key == $errorKey ){
					break;
				};
				if (! file_exists( C\getDBPath($id) )) {
					unlink( C\getDBPath($id) );
				}
			}
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		}

		$this->db->commit();
		return CC\RES::RES_I;
	}
	public function impDb( $filePath ){
		$impDbName = CC\SYSDB::getImpDbName();
		$sqlData = CC\SYSDB::SQL_AND_COLS['INSERT_IMP_DB'];

		//トランザクションの開始。
		$ret = $this->db->begin();
		if( $ret['error'] != null ){
			errLog(
				'CANNOT BEGIN TRANSACTION' ."\t".
				'Exception:'.$ret['error']->getMessage( )."\r\n"
			);
			$ret = CC\RES::RES_W;
			$ret['error'] = 'Database import error';
			$ret['discription'] = 'database name: '.$dbInfo['name'].strOpt;

			errLog(
				'DB FILE CANNOT COPY' ."\t".
				'src:'.CC\FILE::DBTPL."\t".
				'dist:'.C\getDBPath($id)."\r\n"
			);



		}

		$rslt = $this->db->write( $sqlData['SQL'] , [[1=>['value' => $impDbName ,'type' => PDO::PARAM_STR ]]] );
		if( $rslt['error'] != null ){
			$this->db->rollback();
			$errMsgs = [];
			foreach($rslt['error'] as $key => $err){
				$errMsgs[$key] = $err->getMessage( );
			}
			errLog(
				'DB UPDATE ERROR' ."\t".
				'Exception:'.json_encode( $errMsgs )."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		};

		$newId = $this->getTableData('GET_IMP_DB_ID_INFO');
		if( $newId[ 'mode' ] != 'I' ){
			$this->db->rollback();
			return $newId;
		};
		foreach( $newId[ 'table' ] as $row){
			$newId = $row[0];
			break;
		}
		$newDBPath = C\getDBPath( $newId );
		$hasError = ! rename( $filePath , $newDBPath );
		if( $hasError ){
			$this->db->rollback();
			errLog(
				'DB FILE CANNOT MOVE' ."\t".
				'src:'.$filePath."\t".
				'dist:'.$newDBPath."\r\n"
			);
			return CC\RES::RES_SYSERRORS[ 'SYS_F_CONSISTENT' ];
		}
		$this->db->commit();
		return CC\RES::RES_I;
	}
}
