<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

class Spsupgrator extends CI_Controller {

    public $path = "upload/latestversion/zipcontent";
    public $replace_file_path = "upload/latestversion/zipcontent/replace_file";
    public $new_file_path = "upload/latestversion/zipcontent/new_file";
    public $sql_file_path = "upload/latestversion/zipcontent/sql";
    public $sql_shell_file_path = "upload/latestversion/zipcontent/sql_shell";
    public $shell_file_path = "upload/latestversion/zipcontent/shell";
    public $backup_file_path = "upload/backup";
    public $backup_db_path = "upload/backup";
    public $setup_path = "upload/latestversion/zipcontent/setup/setting.php";
    public $new_version = "";
    public $checkResult = true;
    public $failCopy = array();
    public $failQuery = array();
    public $error_log_path = "upload/error_log.txt";
    public $error_log_txt = "";

    public function __construct() {
        error_reporting(0);
        ini_set('max_execution_time', 600);

        $fileName = "application/config/database.php";
        $pattern = '$db[\'default\'][\'db_debug\']';
        $pattern2 = '$db["default"]["db_debug"]';
        $newValue = $pattern . ' = FALSE; ';

        echo "<p><strong>Status : </strong> Setting db config .... </p> <br>";
        if (!$this->updateIni($fileName, $pattern, $newValue)) {
            if (!$this->updateIni($fileName, $pattern2, $newValue)) {
                $this->addLine_file($fileName, $newValue);
            }
        }
        parent::__construct();
    }

    public function removePath($str) {
        if (strpos($str, $this->path) !== false) {
            return str_replace($this->path, '', $str);
        } else {
            return FALSE;
        }
    }

    public function check_version() {
        $v = $this->config->item("application_version");
        include_once $this->setup_path;

        if ($setting['allowed_version']) {
            $list = explode("|", $setting['allowed_version']);

            foreach ($list as $av) {
                if ($av == $v) {
                    if ($setting['new_version']) {
                        $this->new_version = $setting['new_version'];
                        return TRUE;
                    } else {
                        return FALSE;
                    }
                }
            }
            return FALSE;
        }
    }

    public function _splitQueries($query) {
        $buffer = array();
        $queries = array();
        $in_string = false;

        // Trim any whitespace.
        $query = trim($query);

        // Remove comment lines.
        $query = preg_replace("/\n\#[^\n]*/", '', "\n" . $query);

        // Remove PostgreSQL comment lines.
        $query = preg_replace("/\n\--[^\n]*/", '', "\n" . $query);

        // Find function
        $funct = explode('CREATE OR REPLACE FUNCTION', $query);

        // Save sql before function and parse it
        $query = $funct[0];

        // Parse the schema file to break up queries.
        for ($i = 0; $i < strlen($query) - 1; $i++) {
            if ($query[$i] == ";" && !$in_string) {
                $queries[] = substr($query, 0, $i);
                $query = substr($query, $i + 1);
                $i = 0;
            }

            if ($in_string && ($query[$i] == $in_string) && $buffer[1] != "\\") {
                $in_string = false;
            } elseif (!$in_string && ($query[$i] == '"' || $query[$i] == "'") && (!isset($buffer[0]) || $buffer[0] != "\\")) {
                $in_string = $query[$i];
            }
            if (isset($buffer[1])) {
                $buffer[0] = $buffer[1];
            }
            $buffer[1] = $query[$i];
        }

        // If the is anything left over, add it to the queries.
        if (!empty($query)) {
            $queries[] = $query;
        }

        // Add function part as is
        for ($f = 1; $f < count($funct); $f++) {
            $queries[] = 'CREATE OR REPLACE FUNCTION ' . $funct[$f];
        }

        return $queries;
    }

    public function updateIni($fileName, $pattern, $newValue) {
        // $fileName = "application/config/database.php";
        // $pattern_hostname = '$db[\'default\'][\'hostname\']';
        $out = '';
        $pattern_find = FALSE;
        if (file_exists($fileName)) {
            $file = fopen($fileName, 'r+');
            while (!feof($file)) {
                $line = fgets($file);

                if (strpos($line, $pattern) !== false) {
                    $out .= $newValue . PHP_EOL;
                    $pattern_find = TRUE;
                } else {
                    $out .= $line;
                }
            }
            if ($pattern_find) {
                file_put_contents($fileName, $out);
            }

            fclose($file);
        }
        return $pattern_find;
    }

    public function addLine_file($fileName, $line) {
        if (file_exists($fileName)) {
            $current = file_get_contents($fileName);
            $current = $current . PHP_EOL . $line . PHP_EOL;
            file_put_contents($fileName, $current);
        }
    }

    public function xcopy($src, $type = "replace_file/") {
        $directory = glob("{$src}/*"); //scandir($src);
        foreach ($directory as $file) {
            if (is_dir($file)) {
                $this->xcopy($file, $type);
            } else {
                $to = str_replace("upload/latestversion/zipcontent/" . $type, '', $file);
                $a = copy($file, $to);
                if ($a == FALSE) {
                    $this->failCopy[] = "From : " . $file . "; To : " . $to;
                }
            }
        }
    }

    public function run_backup_file($src) {
        $directory = glob("{$src}/*"); //scandir($src);     
        foreach ($directory as $file) {
            if (is_dir($file)) {
                $folder = $this->backup_file_path . "/" . str_replace($this->replace_file_path . "/", '', $file);
                if (!file_exists($folder)) {
                    mkdir($folder);
                }
                $this->run_backup_file($file);
            } else {

                $to = $this->backup_file_path . "/" . str_replace($this->replace_file_path . "/", '', $file);
                copy(str_replace($this->replace_file_path . "/", '', $file), $to);
            }
        }
    }

    public function run_backup_db($path) {
        $hostname = $this->db->hostname;
        $username = $this->db->username;
        $password = $this->db->password;
        $database = $this->db->database;
        if ($password == '') {
            exec("/xampp/mysql/bin/mysqldump  -h $hostname -u $username  $database --force > \"$path\" 2>&1", $output, $err);
        } else {
            exec("/xampp/mysql/bin/mysqldump  -h $hostname -u $username -p$password $database --force > \"$path\" 2>&1", $output, $err);
        }

        return array($err, $output);
    }

    function check_replaceFile($src) {
        $directory = glob("{$src}/*"); //scandir($src);
        if ($directory) {
            foreach ($directory as $file) {
                if (is_dir($file)) {
                    $this->check_replaceFile($file);
                } else {
                    $myfile = str_replace($this->replace_file_path . "/", '', $file);
                    if (!file_exists($myfile) || !is_writable($myfile)) {
                        echo "<strong>File not find  or not modifiable : </strong>" . $myfile . "<br/>";
                        $this->checkResult = FALSE;
                        return FALSE;
                    }
                }
            }
        }
        return TRUE;
    }

    function check_newFile($new_file_path) {
        $directory = glob("{$new_file_path}/*"); //scandir($src);
        if ($directory) {
            foreach ($directory as $file) {
                if (is_dir($file)) {
                    $realPath = str_replace($this->new_file_path . "/", '', $file);
                    if (!file_exists($realPath)) {
                        mkdir($realPath);
                    } else {
                        if (!is_writable($file)) {
                            $this->checkResult = FALSE;
                            return FALSE;
                        }
                    }
                    $this->check_newFile($file);
                }
            }
        }
        return TRUE;
    }

    function check_backupDir($path) {
        $dir = $path . "/bk_" . $this->config->item("application_version") . "_" . date("d-m-Y");
        $this->backup_file_path = $dir;
        $this->backup_db_path = $dir . "/db_backup/db_bkp.sql";
        if (file_exists($dir)) {
            return is_writable($dir);
        } else {
            mkdir($dir, 0777, TRUE);
            return mkdir($dir . "/db_backup", 0777, TRUE);
        }
    }

    function run_sql($path) {
        $directory = glob("{$path}/*"); //scandir($src);
        if ($directory) {
            foreach ($directory as $file) {
                if (is_dir($file)) {
                    $this->run_sql($file);
                } else {
                    // create table and insert default data
                    $fileContent = file_get_contents($file);
                    $sqlArray = array();
                    $sqlArray = $this->_splitQueries($fileContent);
                    if ($sqlArray) {
                        foreach ($sqlArray as $sql) {
                            try {
                                $resul = $this->db->query($sql);
                                if (!$resul) {
                                    $this->failQuery [] = $sql;
                                }
                            } catch (Exception $e) {
                                $this->failQuery [] = $sql;
                            }
                        }
                    }
                }
            }
        }
    }

    function run_sql_shell($path) {
        $directory = glob("{$path}/*"); //scandir($src);
        if ($directory) {
            foreach ($directory as $file) {
                if (is_dir($file)) {
                    $this->run_sql_shell($file);
                } else {
                    $hostname = $this->db->hostname;
                    $username = $this->db->username;
                    $password = $this->db->password;
                    $database = $this->db->database;
                    if ($password == '') {
                        exec("/xampp/mysql/bin/mysql -h $hostname -u $username  $database --force < \"$file\" 2>&1", $output, $err);
                        return array($err, $output);
                    } else {
                        exec("/xampp/mysql/bin/mysql -h $hostname -u $username -p$password $database --force < \"$file\" 2>&1", $output, $err);
                        return array($err, $output);
                    }
                }
            }
        }
    }

    function run_shell($path) {
        $directory = glob("{$path}/*"); //scandir($src);
        if ($directory) {
            foreach ($directory as $file) {
                if (is_dir($file)) {
                    $this->run_shell($file);
                } else {

                    $fileOpened = fopen($file, 'r+');
                    while (!feof($fileOpened)) {
                        $cmd = fgets($fileOpened);
                        if (!is_null($cmd) && $cmd != "") {
                            exec($cmd, $output, $err);
                        }
                    }
                }
            }
        }
    }

    function run_customPhp() {
        
    }

    public function index() {
        header('Content-type: text/html; charset=utf-8');
        error_reporting(0);
        ini_set('max_execution_time', 600);

        $fileName = "application/config/database.php";
        $pattern = '$db[\'default\'][\'db_debug\']';
        $pattern2 = '$db["default"]["db_debug"]';
        $newValue = $pattern . ' = FALSE; ';

        echo "<p><strong>Status : </strong> Setting db config .... </p> <br>";
        if (!$this->updateIni($fileName, $pattern, $newValue)) {
            if (!$this->updateIni($fileName, $pattern2, $newValue)) {
                $this->addLine_file($fileName, $newValue);
            }
        }



        if (!$this->check_version()) {
            echo "<script>
                        alert('Update fail: current version not allowed...');
                        window.location = '" . base_url() . "admin/spsversion';
                  </script>";
            return FALSE;
        }

        echo "<p><strong>Status : </strong> Cheking backup files .... </p> <br>";
        flush();
        ob_flush();
        if (!$this->check_backupDir($this->backup_file_path)) {
            echo "<script>
                        alert('Update fail: backup, permission deny...');
                        window.location = '" . base_url() . "admin/spsversion';
                  </script>";
            return FALSE;
        }

        echo "<p><strong>Status : </strong> Checking  files to be replaced .... </p> <br>";
        flush();
        ob_flush();
        if (file_exists($this->replace_file_path)) {
            $this->check_replaceFile($this->replace_file_path);
            if (!$this->checkResult) {
                echo "<script>
                        alert('Update fail: replace file, permission deny...');
                        window.location = '" . base_url() . "admin/spsversion';
                  </script>";
                return FALSE;
            }
        }

        echo "<p><strong>Status : </strong> Cheking permission of new files .... </p> <br>";
        flush();
        ob_flush();
        if (file_exists($this->new_file_path)) {
            $this->check_newFile($this->new_file_path);
            if (!$this->checkResult) {
                echo "<script>
                        alert('Update fail: new file, permission deny...');
                        window.location = '" . base_url() . "admin/spsversion';
                  </script>";
                return FALSE;
            }
        }
        echo "<p><strong>Status : </strong> backup existing files to be replaced .... </p> <br>";
        flush();
        ob_flush();
        $this->run_backup_file($this->replace_file_path);

        echo "<p><strong>Status : </strong> backup of database .... </p> <br>";
        flush();
        ob_flush();
        $this->run_backup_db($this->backup_db_path);

        echo "<p><strong>Status : </strong> replacing old files .... </p> <br>";
        flush();
        ob_flush();
        if (file_exists($this->replace_file_path)) {
            $this->xcopy($this->replace_file_path);
        }

        echo "<p><strong>Status : </strong> copying new files .... </p> <br>";
        flush();
        ob_flush();

        if (file_exists($this->new_file_path)) {
            $this->xcopy($this->new_file_path, "new_file/");
        }


        if (file_exists($this->sql_file_path)) {
            echo "<p><strong>Status : </strong> running single sql .... </p> <br>";
            flush();
            ob_flush();
            $this->run_sql($this->sql_file_path);
        }

        if (file_exists($this->sql_shell_file_path)) {
            echo "<p><strong>Status : </strong> updating database .... </p> <br>";
            flush();
            ob_flush();
            $this->run_sql_shell($this->sql_shell_file_path);
        }

        if (file_exists($this->shell_file_path)) {
            echo "<p><strong>Status : </strong> running shell  .... </p> <br>";
            flush();
            ob_flush();
            $this->run_shell($this->shell_file_path);
        }

        echo "<p><strong>Status : </strong> custom php .... </p> <br>";
        flush();
        ob_flush();
        $this->run_customPhp();

        $fileName = "application/config/config.php";
        $pattern = '$config[\'application_version\']';
        $pattern2 = '$config["application_version"]';
        $newValue = $pattern . ' = \'' . '' . $this->new_version . '\';';

        echo "<p><strong>Status : </strong> changing version name .... </p> <br>";
        if (!$this->updateIni($fileName, $pattern, $newValue)) {
            if (!$this->updateIni($fileName, $pattern2, $newValue)) {
                $this->addLine_file($fileName, $newValue);
            }
        }


        //Disabled msg
        $this->session->set_userdata('is_latest', "yes");

        if (sizeof($this->failCopy)) {
            echo "<h3><strong>Result : </strong> Fail copy file .... </h3> <br>";
            flush();
            ob_flush();
            foreach ($this->failCopy as $fail) {
                echo "<strong>File : </strong>" . $fail . "<br/>";
            }
        }

        if (sizeof($this->failQuery)) {
            echo "<h3><strong>Result : </strong> Fail query .... </h3>";
            flush();
            ob_flush();
            foreach ($this->failQuery as $fail) {
                echo "<strong>Query : </strong>" . $fail . "<br/>";
                $this->error_log_txt .= $fail . PHP_EOL;
            }
        }

        $fileName = "application/config/database.php";
        $pattern = '$db[\'default\'][\'db_debug\']';
        $pattern2 = '$db["default"]["db_debug"]';
        $newValue = $pattern . ' = TRUE; ';

        echo "<p><strong>Status : </strong> Resetting db config .... </p> <br>";
        if (!$this->updateIni($fileName, $pattern, $newValue)) {
            if (!$this->updateIni($fileName, $pattern2, $newValue)) {
                $this->addLine_file($fileName, $newValue);
            }
        }

        if (!sizeof($this->failCopy) && !sizeof($this->failQuery)) {
            echo "<script>
          window.location = '" . base_url() . "admin2'
          </script>";
        } else {
            $myfile = fopen($this->error_log_path, "w") or die("Unable to open file!");
            fwrite($myfile, $this->error_log_txt);
            fclose($myfile);
            $alert = 'alert("The upgrator has encounter some issues during the processing, \n " +
                     "do email us the error file.\n located in: sps folder/upload/error_log.txt");';
            echo "<script>$alert</script>";

             echo '<strong>The upgrator has encounter some issues during the processing, do email us the error file. located in: sps folder/upload/error_log.txt</strong><br/>';
        }
    }

}
