<?php
require_once("kontrolDB.php");
require_once("kriteria.php");
require_once("kriteria_nilai.php");
require_once("training.php");
 
class mKNN
{
	private $db_handle;
	private $kriteria;
	private $kriteria_nilai;
	private $training;

	public function __construct()
    {
		$this->db_handle = new kontrolDB();
		$this->kriteria = new kriteria();
		$this->kriteria_nilai = new kriteria_nilai();
		$this->training = new training();
    } 

	public function dataTraining()
	{
		$data_training = [];
		$kriteria = [];
		$bobot_kelas = [];
		$kelas = [];

		//Ambil Kelas Klasifikasi
		$result = $this->kriteria->tampil_keputusan();
		foreach ($result as $key => $dataK) {
			$r_nilai = $this->kriteria_nilai->tampil($dataK['id_kriteria']);
			foreach ($r_nilai as $k => $dNilai) {
				$kelas[] = $dNilai['nilai'];
			}
		}

		$sql = "SELECT DISTINCT(kode_training) FROM knn_training ORDER BY kode_training ASC";
		$result = $this->db_handle->getDataAll($sql);
		foreach ($result as $key => $training) {
			$d_train = [];
			$result = $this->kriteria->tampil_kriteria();
			foreach ($result as $key => $dataK) {
				if ($dataK['masukan'] == 1 || $dataK['masukan'] == 2) {
					$r_nilai = $this->training->tampil_satu($training['kode_training'], $dataK['id_kriteria']);
					$d_train[$dataK['id_kriteria']] = (int)$r_nilai[0]['bobot'];
				} else {
					$r_nilai = $this->training->tampil_satu_b($training['kode_training'], $dataK['id_kriteria']);
					$d_train[$dataK['id_kriteria']] = doubleval($r_nilai[0]['masukan']);
					
				}
			}

			$result = $this->kriteria->tampil_keputusan();
			foreach ($result as $key => $dataK) {
				$r_nilai = $this->training->tampil_satu($training['kode_training'], $dataK['id_kriteria']);
				$d_train[$dataK['id_kriteria']] = $r_nilai[0]['nilai'];
				$data_training[$training['kode_training']] = $d_train;
			}
		}
		
		$result = $this->kriteria->tampil_kriteria();
		foreach ($result as $key => $dataX) {
			$kriteria['kriteria'][$dataX['id_kriteria']] = $dataX['kriteria'];
		}
		$result = $this->kriteria->tampil_keputusan();
		foreach ($result as $key => $dataX) {
			$kriteria['keputusan'][$dataX['id_kriteria']] = $dataX['kriteria'];
		}
		//print_r($data_training);				
		//print_r($kriteria);
		//print_r($kelas);
		//print_r($bobot_kelas);				

		$data['data_training'] = $data_training;
		$data['kriteria'] = $kriteria;
		$data['kelas'] = $kelas;
		$data['kelas_bobot'] = $bobot_kelas;

		return $data;
	}

    private function normalisasi($data, $kriteria){
    	// Min-Max Normalization: x' = (x - xmin) / (xmax - xmin)
    	$arr_max=[];
    	$arr_min=[];
		foreach($kriteria['kriteria'] as $k => $krit){
			$arr_max[$k]=max(array_column($data, $k));
			$arr_min[$k]=min(array_column($data, $k));
		}

    	$arr_normal=[];
    	foreach($data as $id => $d){
			foreach($kriteria['kriteria'] as $k => $krit){
				$range = $arr_max[$k] - $arr_min[$k];
				if($range == 0){
					$arr_normal[$id][$k] = 0;
				} else {
					$val = ($d[$k] - $arr_min[$k]) / $range;
					// clamp antara 0 dan 1
					$arr_normal[$id][$k] = max(0, min(1, $val));
				}
			}
			foreach($kriteria['keputusan'] as $k => $krit){
				$arr_normal[$id]['id_class']=$k;
				$arr_normal[$id]['class']=$d[$k];
			}
	  }

    	return array($arr_normal, $arr_max, $arr_min);
    }

  	private function array_sort_by_column(&$arr, $col, $dir = SORT_ASC) {
      $sort_col = array();
      foreach ($arr as $key => $row) {
          $sort_col[$key] = $row[$col];
      }

      return array_multisort($sort_col, $dir, $arr);
  	}
    

    public function _knn ($data_testing, $nk)
    {      
		$dTraining = $this->dataTraining();
		//echo"<pre>";print_r($dTraining);echo"</pre>";
		$data_training = $dTraining['data_training'];
		$kriteria = $dTraining['kriteria'];
		$kelas = $dTraining['kelas'];

		//=====================
    	$data_normalisasi=$this->normalisasi($data_training, $kriteria);
    	$data_normal=$data_normalisasi[0];
		//echo"<pre>";print_r($data_normal);echo"</pre>";
    	$arr_max=$data_normalisasi[1];
    	$arr_min=$data_normalisasi[2];
    	//=====================

		// Normalisasi Min-Max untuk data testing
		$d_testing=[];
		foreach($kriteria['kriteria'] as $k => $krit){
			$range = $arr_max[$k] - $arr_min[$k];
			if($range == 0){
				$d_testing[$k] = 0;
			} else {
				$val = ($data_testing[$k] - $arr_min[$k]) / $range;
				$d_testing[$k] = max(0, min(1, $val));
			}
		}

		$nilai_K=$nk;
		$jarak_eucludian=array();
		foreach($data_normal as $id => $d){

			$jarak=0;
			foreach($kriteria['kriteria'] as $k => $krit){
				$jarak += pow(($d_testing[$k]-$d[$k]), 2);
			}
			$jarak=sqrt($jarak);
	
			// Simpan nilai asli data training
			$nilai_asli = [];
			foreach($kriteria['kriteria'] as $k => $krit){
				$nilai_asli[$k] = isset($data_training[$id][$k]) ? $data_training[$id][$k] : '-';
			}
	
			$jarak_eucludian[$id]=array('jarak'=>$jarak, 'hasil'=>$d['class'], 'nilai_asli'=>$nilai_asli);
		}

		//echo "<pre>"; print_r($jarak_eucludian); echo "</pre>";
		$this->array_sort_by_column($jarak_eucludian, 'jarak');
		//echo "<pre>"; print_r($jarak_eucludian); echo "</pre>";

		$data_terdekat=array_slice($jarak_eucludian, 0, $nilai_K);
		//echo "<pre>"; print_r($data_terdekat); echo "</pre>";  

		$arr_hasil=array();
		$cek_max_distance=0;
		foreach($kelas as $key => $kls){
			$klas=$kls;
			foreach($data_terdekat as $dekat){
				if($dekat['hasil']==$klas){
					$arr_hasil[$klas]=(int)@$arr_hasil[$klas]+1;
				}
			}
		}

		//print_r($arr_hasil);
		//$arr_hasil=array("A"=>0, "B"=>0, "C"=>0);
		if(count($arr_hasil)>0) {
			$maxs = array_keys($arr_hasil, max($arr_hasil));
			//print_r($maxs);

			if(count($maxs)>1){  //Jika ditemukan lebih dari 1 cari yang memiliki nilai terkecil
				$key_terkecil=0;
				$nilai_terkecil=0;
				$i=0;
				foreach($maxs as $k => $kelas){
					$keys=[];
					foreach($data_terdekat as $kode => $dt){
						if($dt['hasil']==$kelas){
							$keys[]=$kode;
						}
					}
					//$keys = array_keys(array_column($data_terdekat, 'hasil'), $kelas);
					//print_r($keys);echo"<br>";
					$isi=[];
					foreach($keys as $k => $ky){
						$isi[$ky]=$data_terdekat[$ky]['jarak'];
					}
					//print_r($isi);
					$mins=array_keys($isi, min($isi));
					//print_r($mins);
					//print_r($data_terdekat);
					if($i==0){
						$nilai_terkecil=$data_terdekat[$mins[0]]['jarak'];
						$key_terkecil=$mins[0];
					} else {
						if($nilai_terkecil>$data_terdekat[$mins[0]]['jarak']){
							$nilai_terkecil=$data_terdekat[$mins[0]]['jarak'];
							$key_terkecil=$mins[0];
						}
					}
					$i++;

				}
				$hasil_akhirnya=@$data_terdekat[$key_terkecil]['hasil'];

			} else {
				//print_r($maxs);
				$hasil_akhirnya=@$maxs[0];
			}

		} else {
			$hasil_akhirnya="";
		}

		return array("hasil"=>$hasil_akhirnya, "data_terdekat"=>$data_terdekat, "jumlah"=>$arr_hasil);

    }

}
?>