<?php

// Namespace
namespace BMI\Plugin\Database;

// Use
use BMI\Plugin\BMI_Logger AS Logger;
use BMI\Plugin\Progress\BMI_ZipProgress AS Progress;
use BMI\Plugin\Backup_Migration_Plugin as BMP;

// Exit on direct access
if (!defined('ABSPATH')) exit;

/**
 * BMI_Database
 */
class BMI_Database {

  function __construct($host, $user, $pass, $name) {

    $this->host = $host;
    $this->user = $user;
    $this->pass = $pass;
    $this->name = $name;

  }

  public function exportDatabase($backup_name = false) {

    $mysqli = new \mysqli($this->host, $this->user, $this->pass, $this->name);
    $mysqli->select_db($this->name);
    $mysqli->query("SET NAMES 'utf8'");

    $file = fopen(BMI_TMP . DIRECTORY_SEPARATOR . $backup_name, 'w');
    $queryTables = $mysqli->query('SHOW TABLES');
    while ($row = $queryTables->fetch_row()) $target_tables[] = $row[0];

    fwrite($file, "SET SQL_MODE = \"\";\r\n");
    fwrite($file, "SET time_zone = \"+00:00\";\r\n\r\n\r\n");
    fwrite($file, "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\r\n");
    fwrite($file, "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\r\n");
    fwrite($file, "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\r\n");
    fwrite($file, "SET SQL_MODE = \"\";\r\n");
    fwrite($file, "/*!40101 SET NAMES utf8 */;\r\n");
    fwrite($file, "--\r\n-- Database: `" . $this->name . "`\r\n");
    fwrite($file, "--\r\n\r\n\r\n");

    foreach ($target_tables as $table) {

      if (empty($table)) { continue; }

      $chunks_req	= $mysqli->query('SELECT count(1) FROM `'.$table.'`');
      $chunks_res = mysqli_fetch_array($chunks_req);
      $chunks = intval($chunks_res[0]);

      $res = $mysqli->query('SHOW CREATE TABLE `' . $table . '`');
      $TableMLine = $res->fetch_row();
      fwrite($file, "\n\n".$TableMLine[1].";\n\n");
      $TableMLine[1] = str_ireplace('CREATE TABLE `','CREATE TABLE IF NOT EXISTS `', $TableMLine[1]);

      if ($chunks > 0) {
        $g = 0;
        while($g < $chunks) {
          if (($g + 100) > $chunks) $limit_str = 'LIMIT '. $g . ',1000';
          else $limit_str = 'LIMIT '. $g . ',100';
          $this->saveDatabaseFields($mysqli, $table, $file, $limit_str);
          $g += 100;
        }
      } else $this->saveDatabaseFields($mysqli, $table, $file);

      fwrite($file, "\n\n\n");

    }

    fwrite($file, "\r\n\r\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\r\n");
    fwrite($file, "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\r\n");
    fwrite($file, "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;");

    $mysqli->close();
    $backup_name = $backup_name ? $backup_name : $this->name.'___('.date('H-i-s').'_'.date('d-m-Y').').sql';
    fclose($file);

  }

  public function saveDatabaseFields(&$mysqli, $table, &$file, $limit = '') {

    $result	= $mysqli->query('SELECT * FROM ' . BMP::escapeSQLIDentifier($table) . ' ' . $limit);
    $fields_amount = $result->field_count;
    $rows_num = $mysqli->affected_rows;
    for ($i = 0, $st_counter = 0; $i < $fields_amount; $i++, $st_counter = 0) {
      while ($row = $result->fetch_row())	{

        if ($st_counter % 100 == 0 || $st_counter == 0) {
          fwrite($file, "\nINSERT INTO " . BMP::escapeSQLIDentifier($table) . " VALUES");
        }

        fwrite($file, "\n(");
        for ($j = 0; $j < $fields_amount; $j++) {
          $row[$j] = str_replace("\n","\\n", addslashes($row[$j]));

          if (isset($row[$j])) fwrite($file, '"'.$row[$j].'"');
          else fwrite($file, '""');

          if ($j<($fields_amount-1)) fwrite($file, ',');
        }

        fwrite($file, ")");
        if ((($st_counter+1)%100==0 && $st_counter!=0) || $st_counter+1==$rows_num) {

          fwrite($file, ";");

        } else fwrite($file, ",");

        $st_counter = $st_counter + 1;

      }
    }

  }

  public function recursiveReplace(&$line, &$at, &$from, &$to) {

    $end = -1;
    for ($i = $at; $i > 3; --$i) {
      $str = $line[$i-2] . $line[$i-1] . $line[$i];
      if ($str === ':\"') {
        $end = $i;
        break;
      }
      unset($str);
    }

    $start = -1; $semi = false;
    for ($i = $end; $i > 0; --$i) {
      $str = $line[$i] . $line[$i+1] . $line[$i+2];
      if ($str === ';s:') {
        $semi = true;
        $start = $i + 3;
        break;
      }
      if ($str === '{s:') {
        $start = $i + 3;
        break;
      }
      unset($str);
    }

    if ($start == -1 || $end == -1) return $line;
    $diff = $end - $start; $eof = -1;
    $len = intval(substr($line, $start, $diff));
    $maxend = strlen($line);
    if ($maxend > ($end + 3 + $len + 6)) {
      $maxend = ($end + 3 + $len + 6);
    }

    for ($j = ($end + 3); $j < $maxend; ++$j) {
      $str = $line[$j] . $line[$j+1] . $line[$j+2];
      if ($str === '\";') {
        $eof = $j;
        break;
      }
      unset($str);
    }

    if ($eof == -1) {
        for ($i = $maxend; $i > ($end + 3); --$i) {
        $str = $line[$i] . $line[$i+1] . $line[$i+2];;
        if ($str === '\";') {
          $eof = $i;
          break;
        }
        unset($str);
      }
    }

    if ($eof == -1 || $eof < $end || $eof < $start) return $line;
    $should = abs(strlen($from) - strlen($to));
    $toreplace = substr($line, $start - 3, (($eof + 6) - $start));
    $content = str_replace($from, $to, substr($toreplace, $diff + 6, -3));
    if ($semi) $newreplace = ';s:' . strlen($content) . ':\"' . $content . '\";';
    else $newreplace = '{s:' . strlen($content) . ':\"' . $content . '\";';
    $line = str_replace($toreplace, $newreplace, $line);
    // if (strlen($len >= $content)) {

    // error_log($start . ' - ' . $end . ' - ' . $eof . ' - ' . $len . '['.strlen($toreplace).']' . ' - ' . '['.strlen($newreplace).']');
    // error_log(strlen($toreplace) . ' -> ' . strlen($newreplace));
    // error_log($toreplace);
    // error_log($newreplace);
    // error_log('');
    // }

    // Do the same for every occurence
    $d = strpos($line, $from);
    if ($d !== false) $line = $this->recursiveReplace($line, $d, $from, $to);

    return $line;
  }

  public function replaceInLine(&$line) {

    if (strpos($line, ':\"') !== false) {

      $d = strpos($line, $this->old_domain);
      if ($d !== false) $this->recursiveReplace($line, $d, $this->old_domain, $this->new_domain);

      $b = strpos($line, $this->abs);
      if ($b !== false) $this->recursiveReplace($line, $b, $this->abs, $this->newabs);

    } else {

      // Replace in case user had this wrongly defined in wp-config.
      // Of course, those two ifs will take a lot of memory...
      if (substr($line, 0, 16) === '("1","siteurl","') $line = '("1","siteurl","' . $this->new_domain . '","yes"),';
      if (substr($line, 0, 13) === '("2","home","') $line = '("2","home","' . $this->new_domain . '","yes"),';

      $line = str_replace($this->old_domain, $this->new_domain, $line);
      $line = str_replace($this->abs, $this->newabs, $line);

    }

    return $line;
  }

  public function importDatabase($file, $old_domain, $new_domain, $abs, $newabs, $newprefix, $siteurl, $home) {

    if (BMP::isFunctionEnabled('ignore_user_abort')) @ignore_user_abort(true);
    if (BMP::isFunctionEnabled('set_time_limit')) @set_time_limit(16000);
    if (BMP::isFunctionEnabled('ini_set')) {
      @ini_set('max_execution_time', '259200');
      @ini_set('max_input_time', '259200');
    }

    $SQL_CONTENT = $file;
    $mysqli = new \mysqli($this->host, $this->user, $this->pass, $this->name);

    if (mysqli_connect_errno()) {
      Logger::error(__("Failed to connect to MySQL: ", 'backup-backup') . mysqli_connect_error());
      return __("Importing failed - cannot connect to the database.", 'backup-backup');
    }

    $this->old_domain = $old_domain;
    $this->new_domain = $new_domain;
    $this->escaped_old = str_replace('/', '\\\/', $old_domain);
    $this->escaped_new = str_replace('/', '\\\/', $new_domain);

    $this->abs = $abs;
    $this->newabs = $newabs;
    $this->e_abs_old = str_replace('/', '\\\/', $abs);
    $this->e_abs_new = str_replace('/', '\\\/', $newabs);

    $handle = fopen($file, 'r');
    if ($handle) {
      $templine = '';
      $qyr = $mysqli->query('SET foreign_key_checks = 0');

      while (($line = fgets($handle)) !== false) {
        preg_match_all("/\nCREATE TABLE(.*?)\`(.*?)\`/si", "\n" . $line, $target_tables);
        foreach ($target_tables[2] as $table) $mysqli->query('DROP TABLE IF EXISTS `' . $table . '`');
        if (substr($line, 0, 2) != '--' && $line != '') {
          $this->replaceInLine($line);
          $templine .= $line;
          if (substr(trim($line), -1, 1) == ';') {
            if (!$mysqli->query($templine)) {
              Logger::error('Error performing query ' . $templine . ': ' . $mysqli->error);
            }
            $templine = '';
            $line = '';
          }
        }
      }

      fclose($handle);
    } else {
      Logger::error(__("Cannot open SQL file...", 'backup-backup'));
      return __("Importing failed - Cannot open SQL file...", 'backup-backup');
    }

    $qyr = $mysqli->query('SET foreign_key_checks = 1');
    $mysqli->query("SET NAMES 'utf8'");

    // $mysqli->query("UPDATE `".$new_prefix."options` SET option_value = '" . $siteurl. "' WHERE option_name = 'siteurl'");
    // $mysqli->query("UPDATE `".$new_prefix."options` SET option_value = '" . $home. "' WHERE option_name = 'home'");

    $mysqli->close();
    return __("Importing finished. Now, Delete the import file.", 'backup-backup');

  }

}
