<?php 
function xunit($args = [],$parent = null){
	return new xunit($args,$parent);
}
class xunit{
	var $args = [];
	var $parent = null;
	function __construct($args = [],$parent = null){
		$this->args = $args;
		$this->parent = $parent;
	}
	var $_dataCall = [];
	function __call($name,$args){
		if(empty($this->_dataCall[$name])){
			$this->_dataCall[$name] = [];
		}
		$this->_dataCall[$name][] = $args;
		return $this;
	}
	function __get($name){
		return $this->args[$name];
	}
	function __set($name,$value){
		$this->args[$name] = $value;
	}
	function getClassName(){
		return get_class($this);
	}
	function set($name,$value = null){
		$this->$name = $value;
		return $this;
	}
	function new($name,$args = []){
		$this->$name = xunit($args,$this);
		return $this->$name;
	}
}
class xtableview extends xunit{
	var $name = "";
	function __construct($name){
		$this->setName($name);
	}
	function getName(){
		return $this->name;
	}
	function setName($value){
		$this->name = $name;
	}
	function getDirectiveNameHTML(){
		
	}
	function getDirectiveNameJS(){
		
	}
}

api::xtexttable(function($data){
	$article = new xtableview("article");
	$article->addAction([
		"name"     => "details",
		"template" => function(){/* JS */}
	]);
	$article->view([
		"default" => function(){}
	]);
	$article->add([
		"name"  => "",
		"titre" => "",
		"form"  => [
			"show"       => 1,
			"order"      => 1,
			"disabled"   => 0,
			"col"        => 3,
			"template"   => function(){}
		],
		"filter" => [
			"show"       => 1,
			"order"      => 1,
			"disabled"   => 0,
			"template"   => function(){}
		],
		"table" => [
			"show"       => 1,
			"order"      => 1,
			"flex"       => true,
			"template"   => function(){}
		],
		"db" => function($table){
			$table->double("totalstock");
			$table->json("stocks");
		},
		"filter" => [
			"alert" => "IFNULL(totalstock)<=1",
			function($filter){}
		],
		"events" => [
			"on-before-insert" => function(){},
			"on-before-update" => function(){},
			"on-before-save"   => function(){},
			"on-before-delete" => function(){},
			"on-after-insert"  => function(){},
			"on-after-update"  => function(){},
			"on-after-save"    => function(){},
			"on-after-delete"  => function(){},
			"on-load"          => function(){},
			"on-validation"    => function(){},
			"on-js-init"       => function(){/* JS */},
			"on-js-validation" => function(){/* JS */},
			"on-js-load"       => function(){/* JS */},
			"on-js-change"     => function(){/* JS */},
			"on-js-send-api"   => function(){/* JS */},
		]
	])
	->fn("name",function(){})
	->fn("name",function(){})
	->fn("name",function(){})
	->fn("name",function(){})
	;





	var_dump(get_object_vars($article));
	return;
	echo json_encode($article);

});



function tableview($name){
	return new tableview($name);
}
class tableview{
	var $name = "";
	function __construct($name){
		$this->name = $name; 
	}
	var $_api = null;
	var $_tablename = null;
	var $_actions = [];
	function api($fn,$tablename=null,$actions = ["save","delete"]){
		$this->_api = $fn;
		$this->_tablename = $tablename;
		$this->_actions = is_string($actions)?explode(",",$actions):$actions;
		return $this;
	}

	function getSql($data){
		if (empty($this->_api)) {
			if (is_callable($this->_sql)) {
				$fn = $this->_sql;	
				return $fn($data);
			}elseif(is_string($this->_sql)){
				return $this->_sql;
			}
		}
		return $this->_api;
	}
	var $_sql = "";
	function sql($fn,$tablename=null,$actions = ["save","delete"]){
		$this->_sql = $fn;
		$this->_tablename = $tablename;
		$this->_actions = is_string($actions)?explode(",",$actions):$actions;
		return $this;
	}
	var $_subRep = [];
	function addRep($name,$fql){
		$this->_subRep[] = [
			"name" => $name,
			"fql" => $fql
		];
		return $this;
	}

	var $_tpl = null;
	function tpl($fn){
		$this->_tpl = $fn;
		return $this;
	}
	var $_view = [];
	function view($name,$fn){
		$this->_view[$name] = $fn;
		return $this;
	}
	var $_js = null;
	function js($fn){
		$this->_js = $fn;
		return $this;
	}
	var $_scope = null;
	function scope($fn){
		$this->_scope = $fn;
		return $this;
	}
	var $_ctrl = null;
	function ctrl($fn){
		$this->_ctrl = $fn;
		return $this;
	}
	function getScript($name){
		ob_start();
		$fn = $this->{"_$name"};
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			$fn();
		}else{
			echo $fn;
		}
		return Route::rmvTag(ob_get_clean());
	}
	var $_route = null;
	function route($root,$view = "Backoffice"){
		$this->_route = [
			"route" => $root,
			"view" => $root,
		];
		return $this;
	}
	function getTpl(){
		$fn = $this->_tpl;
		if(is_callable($fn)){
			$fn = $fn->bindTo($this);
			ob_start();
			$fn();
			$fn=ob_get_clean();
		}
		ob_start();
	?>
		<div>
			<style type="text/css">
				.filter{
					display: flex;
					align-items: flex-end;
				}
				.filter > .form-group,.filter > * > .form-group{
					margin: 0;
				}
				.filter > *{
					margin: 0;
				}
				.filter > *+*{
					margin-left: 5px;
				}
				.col-action{
					display: flex;
					align-items: center;
				}
				.col-action > *+*{
					margin-left: 5px;
				}
			</style>
			<api-data ng-model="mdl" api="apitv_<?= $this->name ?>" on-responce="ctrl.api_data_responce(mdl)" filter="filter" init-item="_initItem">
 				<div ng-switch="ctrl.view">
					<?php foreach ($this->_view as $key => $value) { ?>
					<div ng-switch-when="<?= $key ?>">
						<?php 
						if(is_callable($value)){
							$value = $value->bindTo($this);
							$value();
						}else{
							echo $value;
						}
						?>
					</div>
					<?php } ?>  
					<div ng-switch-default>
						<?= $fn ?>
					</div>
				</div>
			</api-data>
		</div>
	<?php
		return json_encode(preg_replace("/\t|\n|\r/i", "", ob_get_clean()));
	}
	function getSqlAfterFilter($sql,$filter,&$dataSql){
		if($row = db::row("SELECT * FROM ($sql) t78765 limit 0,1")){
			$cols = array_keys($row);
			$where = [];
			foreach ($cols as $col) {
				if (isset($filter[$col])) {
					if (is_array($filter[$col])) {
						if (isset($filter[$col]["id"])) {
							if (in_array("id_$col", $cols)) {
								$where[] = "id_$col = :$col";
								$dataSql[":$col"] = $filter[$col]["id"];
							}
						}
					}elseif(is_string($filter[$col])){
						if(!empty($filter[$col])){
							$where[] = "$col = :$col";
							$dataSql[":$col"] = $filter[$col];
						}
					}else{
						$where[] = "$col = :$col";
						$dataSql[":$col"] = $filter[$col];
					}
				}
			}
			if(!empty($where)){
				$where = join(" AND ", $where);
				$sql = "SELECT * FROM ($sql) t78765 where $where";
			}
		}
		return $sql;
	}
	function run(){
		$self = $this;
		app::api("apitv_$this->name",function($data) use ($self){
			if(empty($data["filter"])) $data["filter"] = [];
			if(empty($data["filter"]["length"])){
				$data["filter"]["length"] = 20;
				$data["length"] = 20;
			}else{
				$data["length"] = $data["filter"]["length"];
			}
			$api = $self->getSql($data);
			$filter = $data["filter"];
			$item = $data["item"];
			$action = $data["action"];
			if (is_callable($api)) {
				$api = $api->bindTo($self);
				return $api($action,$filter,$item,$data);
			}elseif(is_string($api)){
				switch ($action) {
					case 'save':
						if(!$self->_tablename || !in_array("save",$self->_actions)) return;
						Event::run($self->_tablename."_BeforeSave",$item);
						$item["id"] = db::save($self->_tablename,$item);
						Event::run($self->_tablename."_AfterSave",$item);
						return [
							"ok" => true,
							"item" => $item
						];
					break;
					case 'delete':
						if(!$self->_tablename || !in_array("delete",$self->_actions)) return;
						Event::run($self->_tablename."_BeforeDelete",$item["id"]);
						dbq($self->_tablename)->delete($item["id"]);
						Event::run($self->_tablename."_AfterDelete",$item["id"]);
						return true;
					break;
					default:
						$dataSql = [];
						$api = $self->getSqlAfterFilter($api,$filter,$dataSql);				
						$rep = db::pagination($api,$data,$dataSql);
						foreach ($self->_subRep as $fql) {
							if(is_callable($fql["fql"])){
								$fn = $fql["fql"];
								$fql["fql"] = $fn($api,$data);
							}
							$dataSql1 = [];
							$sql = $self->getSqlAfterFilter($fql["fql"],$filter,$dataSql1);
							$rep[$fql["name"]] = ["list"=>[],"first"=>null];
							$rep[$fql["name"]]["list"] = db::table($sql,$dataSql1);
							$rep[$fql["name"]]["first"] = count($rep[$fql["name"]]["list"])>0?$rep[$fql["name"]]["list"][0]:null;
						}
						return $rep;
					break;
				}
			}
		});
		Route::isJs(function() use ($self){
			$js = $self->_js;
			if(is_callable($js)){
				$js = $js->bindTo($self);
				$js();
			}else{
				?>
				<script>
					app.directive('<?= $self->name ?>', ['$sce',function ($sce) {
						return {
							restrict: 'EA',
							template : <?= $self->getTpl() ?>,
							scope : {ngModel : "=?",view:"@?", rules : "=?" <?= $self->getScript("scope") ?> },
							controller : function($scope){
								$scope._tableview = "<?= $self->name ?>";
								$scope.ngModel = $scope;
								$scope.ctrl = $scope;
								$scope.filter = $scope.filter || {};
								$scope.can = {
									add : 2,
									update : 2,
									delete : 2,
									show : 2
								};
								$scope.$watch("rules", function(){
									if($scope.rules === true){
										$scope.can = {
											add : 2,
											update : 2,
											delete : 2,
											show : 2
										};
									}else if($scope.rules){
										var dd = ["add","update","delete","show"];
										for (var i = 0; i < dd.length; i++) {
											var k = dd[i];
											$scope.can[k] = $scope.rules[k] * 1;
										}
									}
								},true);
								$scope.api_data_responce = function(mdl){
									if($scope.api_responce) $scope.api_responce();
								}
								$scope.$action = function(name,data,success,error,reload,accept){
									$scope.mdl.loading = true;
									api("apitv_<?= $this->name ?>",{action:name,data:data},function(rep){
										$scope.mdl.loading = false;
										if(rep && (rep.success || rep.ok || rep === true)){
											if(reload){
												$scope.mdl.loadData();
											}
											if(accept && rep && rep.scope){
												for(var x in rep.scope){
													$scope[x] = rep.scope[x];
												}
											}
											if(success) success();
										}else{
											if(error) error();
										}
										if($scope.api_responce) $scope.api_responce();
										$scope.$apply();
									});
								}
								$scope._initItem = function(item){
									item.$loading = false;
									item.$action = function(action,success,error,reload,accept){
										item.$loading = true;
										api("apitv_<?= $this->name ?>",{action:action,item:item},function(rep){
											item.$loading = false;
											if(rep && (rep.success || rep.ok || rep === true)){
												if(reload){
													$scope.mdl.loadData();
												}
												if(accept && rep && rep.item){
													for(var x in rep.item){
														item[x] = rep.item[x];
													}
												}
												if(success) success();
											}else{
												if(error) error();
											}
											$scope.$apply();
										});
									}
									item.$save = function(success,error){
										item.$action("save",success,error,!item.id,!!item.id);
									}
									item.$delete = function(success,error){
										if(!confirm("Voulez vous vraiment supprimer cet enregistrement?")) return;
										item.$action("delete",success,error,true);
									}
									if($scope.initItem) $scope.initItem(item);
								}
								<?= $self->getScript("ctrl") ?>

							}
						};
					}]);
				</script>
				<?php
			}
		});
		if ($this->_route) {
			Route::is($this->_route["route"],function($data) use ($self){
				view::use($self->_route["view"]);
				$mdlname = db_table::from_camel_case($self->name);
				?>
				<<?= $mdlname ?> view="select" widget="<?= $self->name ?>" ></<?= $mdlname ?>>
				<?php
			});
		}
	}
}



function dbq($table){
	return new dbq($table);
}

class dbq{
	var $table = "";
	function __construct($tablename){
		$this->table = $tablename;
	}
	function delete($id){
		if (is_numeric($id)) {
			return db::query("DELETE FROM $this->table where id = :id",[":id"=>$id]);
		}elseif(is_array($id)){
			$where  = [];
			$data = [];
			foreach ($id as $key => $value) {
				$where[] = " $key = :$key";
				$data[":$key"] = $value;
			}
			if(empty($where)) return false;
			$where = join(" AND ",$where);
			return db::query("DELETE FROM $this->table where $where",$data);
		}elseif(is_string($id) && !empty($id)){
			return db::query("DELETE FROM $this->table where $id");
		}
	}
	function select($cols = "*"){

	}
	function pagination($data){

	}
}






Route::isJs(function(){
ob_start();
?>
<div style="background: #fff;min-height: 300px;border: 1px solid #ccc;">
	<md-progress-linear class="hideonprint" style="margin: 0;width: 100%;" value="0" md-mode="{{mdl.loading?'indeterminate':'determinate'}}"></md-progress-linear>
	<table class="table table-striped" style="margin:0;background: #fff;">
		<tr>
			<th ng-repeat="col in cols" ng-hide="col.hideif" style="width: {{col.flex?'auto':'1px'}};white-space: nowrap;align-text:{{col.align}};{{col.style}};{{col.thStyle}};" ng-class="{'hideonprint':col.tcoltype=='checkbox'}">{{col.label}}</th>
		</tr>
		<tr ng-repeat="item in mdl.data | filter:(mdl.filterRepeat || {})" ng-class="((item.$class=item.$class||{}) && ((item.$class.success=item.$checked) || true) && item.$class)">
			<td ng-repeat="col in cols" ng-hide="col.hideif" style="width: {{col.flex?'auto':'1px'}};cursor: pointer;white-space: nowrap;text-align:{{col.align}};{{col.style}};{{col.tdStyle}};"  ng-click="clickColChecked(item,col)" ng-class="{'hideonprint':col.tcoltype=='checkbox'}">
				<div ng-switch="col.tcoltype">
					<div ng-switch-when="checkbox">
						<md-checkbox ng-model="item.$checked" ng-disabled="item.$disabled" ng-change="updateListChecked(item)" aria-label="checked" style="margin: 0;"></md-checkbox>
					</div>
					<div ng-switch-default>
						<div bind-html-compile="col.html" ng-class="col.ngClass"></div>
					</div>
				</div>
			</td>
		</tr>
	</table>
	<ng-transclude></ng-transclude>
	<div ng-repeat="f in forms">
		<dialog ng-model="ctrl.form[f.name]" titre="{{f.label}}" btn-fermer="true" btn-valider="true" ng-valider="f.ngValider?f.ngValider({fermer:fermer}):false">
			<div bind-html-compile="f.html"></div>
		</dialog>
	</div>
</div>
<?php
$code=ob_get_clean();
?>
<script>

	app.directive('tableView', ['$sce',function ($sce) {
		return {
			restrict: 'E',
			template : <?= json_encode($code) ?>,
			replace : true,
			transclude : true,
			controller : function($scope){
				function getParentTableView($s,i){
					if(!i) i = 0;
					if($s._tableview){
						return $s;
					}
					if ($s.$parent && i<10) return getParentTableView($s.$parent,i+1);
					return false;
				}
				$scope.ctrl = getParentTableView($scope.$parent.$parent) || $scope;
				$scope.checkedList = [];
				$scope.updateListChecked = function(item){
					if(!$scope.havecheckbox) return;
					$scope.havecheckbox.checkedList = $scope.havecheckbox.checkedList || [];
					for (var i = 0; i < $scope.havecheckbox.checkedList.length; i++) {
						if ($scope.havecheckbox.checkedList[i].id == item.id) {
							$scope.havecheckbox.checkedList.splice(i,1);
						}
					}
					if(item.$checked){
						if($scope.havecheckbox.multi){
							$scope.havecheckbox.checkedList.push(item);
						}else{
							$scope.ctrl.mdl.data.forEach(function(elm){elm.$checked = false;});
							item.$checked = true;
							$scope.havecheckbox.checkedList = [item];
						}
					}else{
						if(!$scope.havecheckbox.multi){
							$scope.havecheckbox.checkedList =  [];
						}
					}
					$scope.checkedList = $scope.havecheckbox.checkedList;
				}
				$scope.clickColChecked = function(item,col){
					if(col.tcoltype == "checkbox" || col.tcoltype == "action" || item.$disabled) return;
					item.$checked = !item.$checked;
					$scope.updateListChecked(item);
				}

				$scope.ctrl.form = {};
				$scope.ctrl.cols = [];
				$scope.havecheckbox = false;
				this.addCol = function($col){
					if ($col.tcoltype == "checkbox") {
						$scope.havecheckbox = $col;
					}
					$scope.ctrl.cols.push($col);
				}
				$scope.ctrl.forms = [];
				this.addForm = function($col){
					$scope.ctrl.forms.push($col);
				}
				var initItem = $scope.ctrl.initItem;
				$scope.ctrl.initItem = function(item){
					item.$check = function(v){
						if (v === undefined) {
							v = !item.$checked;
						}
						if (v) {
							if(!$scope.checkedList.find(function(elm){return elm.id == item.id;})){
								$scope.checkedList.push(angular.copy(item));
							}
						}else{
							for (var i = 0; i < $scope.checkedList.length; i++) {
								if ($scope.checkedList[i].id == item.id) {
									$scope.checkedList.splice(i,1);
									break;
								}
							}
						}
						item.$checked = v;
					}
					item.$checked = false;
					$scope.checkedList.forEach(function(elm){
						if (elm.id == item.id) {
							item.$checked = true;
						}
					});
					$scope.ctrl.forms.forEach(function(f){
						item['$'+f.name] = function(){
							$scope.ctrl.item = item;
							$scope.ctrl.form[f.name].show();
						};
					});
					if(initItem) initItem(item);
				}
				$scope.ctrl.new = function(item){
					$scope.ctrl.item = item || {};
					$scope.ctrl.mdl.initItemBase($scope.ctrl.item);
					return $scope.ctrl.item;
				}
				$scope.ctrl.add = $scope.ctrl.new;
			},
			link: function ($scope, iElement, iAttrs) {
				
			}
		};
	}]);

	app.directive('tform', ['$sce',function ($sce) {
		return {
			restrict: 'E',
			require : "^tableView",
			template : "<span><ng-transclude></ng-transclude></span>",
			scope : {
				label : "@?",
				name : "@?",
				ngValider : "&?"
			},
			transclude : true,
			replace : true,
			controller : function($scope){

			},
			compile : function(tElement, tAttrs){
		       	return {
		             post: function($scope, element, iAttrs, tableView, transcludeFn){
		             	$scope.html = content = element[0].children[0].innerHTML;
		             	element.html("");
						tableView.addForm($scope);
		             }
		        };
			} 
		};
	}]);


	app.directive('tcol', ['$sce',function ($sce) {
		return {
			restrict: 'E',
			require : "^tableView",
			template : "<span><ng-transclude></ng-transclude></span>",
			scope : {
				label : "@?",
				style : "@?",
				ngClass : "=?",
				tdStyle : "@?",
				name : "@?",
				align : "@?",
				tcoltype : "@?",
				hideif:"=?"
			},
			transclude : true,
			replace : true,
			controller : function($scope){

			},
			compile : function(tElement, tAttrs){
		       	return {
		             post: function($scope, element, iAttrs, tableView, transcludeFn){
		             	$("md-checkbox",element[0].children[0]).each(function(){
		             		$(this).html("");
		             	});
		             	$scope.html = content = element[0].children[0].innerHTML;
		             	element.html("");
						$scope.flex = 'flex' in iAttrs;
						tableView.addCol($scope);
		             }
		        };
			} 
		};
	}]);

	app.directive('tcheckbox', ['$sce',function ($sce) {
		return {
			restrict: 'E',
			require : "^tableView",
			template : "<span><ng-transclude></ng-transclude></span>",
			scope : {
				checkedList : "=?",
				multi : "=?",
				hideif:"=?"
			},
			transclude : true,
			replace : true,
			controller : function($scope){

			},
			compile : function(tElement, tAttrs){
		       	return {
		             post: function($scope, element, iAttrs, tableView, transcludeFn){
		             	element.html("");
		             	$scope.tcoltype = "checkbox"; 
		             	$scope.label = "";
						tableView.addCol($scope);
		             }
		        };
			} 
		};
	}]);
</script>
<?php
});







Route::isJs(function(){
	ob_start();
	?>
	<div class="form-group avs">
		<label style="display:block" class="control-label">{{label || 'Chercher'}}</label>
		<input type="text" class="form-control avs" ng-model="ngModel" placeholder="{{label || 'Chercher'}}">
	</div>
	<?php
	$code=ob_get_clean();
	?>
	<script>
		app.directive('filterTxt', ['$sce',function ($sce) {
			return {
				restrict: 'EA',
				transclude : false,
				replace : false,
				template : <?= json_encode(preg_replace("/\t|\n|\r/i", "", $code)) ?>,
				scope : {
					cols : "@",
				},
				controller : function($scope){
					
				},
				link: function (scope, iElement, iAttrs) {
					
				}
			};
		}]);
	</script>
	<?php
});


