<?php

use EesyPHP\Check;
use EesyPHP\Log;
use EesyPHP\Session;
use EesyPHP\Tpl;
use EesyPHP\Url;

/*
 * Check values helpers
 */
function check_status($status) {
  global $status_list;
  return array_key_exists($status, $status_list);
}

/*
 * Handling item POST data
 */
function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=null, &$item=null,
                               &$changes=null) {
  $field_errors=array();
  if (isset($_POST['submit'])) {
    Log :: debug('POST data : '.vardump($_POST));
    // Name
    if (!$enabled_fields || in_array('name', $enabled_fields)) {
      if (isset($_POST['name'])) {
        if (Check :: name($_POST['name'])) {
          $info['name'] = $_POST['name'];
        }
        else {
          $field_errors['name'] = "Ce nom est invalide.";
        }
      }
      else {
        $field_errors['name'] = "Cette information est obligatoire.";
      }
    }

    // status
    if (!$enabled_fields || in_array('status', $enabled_fields)) {
      if (isset($_POST['status']) && check_status($_POST['status'])) {
        $info['status'] = $_POST['status'];
      }
      else {
        $field_errors['status'] = "Cette information est obligatoire.";
      }
    }

    // description
    if (
      isset($_POST['description']) &&
      (!$enabled_fields || in_array('description', $enabled_fields))
    ) {
      if (Check :: is_empty(trim($_POST['description']))) {
        $info['description'] = null;
      }
      else if (Check :: description($_POST['description'])) {
        $info['description'] = $_POST['description'];
      }
      else {
        $field_errors['description'] = "Cette description est invalide.";
      }
    }
  }

  // Check custom required fields
  if (is_array($required_fields)) {
    foreach ($required_fields as $field) {
      if (array_key_exists($field, $field_errors))
        continue;
      if (array_key_exists($field, $info) && !is_null($info[$field]) && !Check :: is_empty($info))
        continue;
      $field_errors[$field] = "Cette information est obligatoire.";
    }
  }

  if (empty($field_errors) && is_array($item) && !is_null($changes)) {
    $changes = array();
    foreach ($info as $key => $value) {
      if ($value != $item[$key])
        $changes[$key] = $value;
    }
  }
  return $field_errors;
}

/*
 * Parser/formater values helpers
 */
$_date_format = "%d/%m/%Y";
$_date_time_format = "%d/%m/%Y %H:%M:%S";
function format_time($time, $with_time=true) {
  global $_date_format, $_date_time_format;
  if ($with_time)
    return strftime($_date_time_format, $time);
  return strftime($_date_format, $time);
}

function parse_date($date, $with_time=true) {
  global $_date_format, $_date_time_format;
  if ($with_time)
    $ptime = strptime($date, $_date_time_format);
  else
    $ptime = strptime($date, $_date_format);
  if(is_array($ptime)) {
    return mktime(
      $ptime['tm_hour'],
      $ptime['tm_min'],
      $ptime['tm_sec'],
      $ptime['tm_mon']+1,
      $ptime['tm_mday'],
      $ptime['tm_year']+1900
    );
  }
  return false;
}

function format_size($size, $digit=False) {
  if (!$digit && $digit!==0) $digit=2;
  if ($size>=1099511627776)
    return number_format($size/1099511627776,$digit)."To";
  elseif ($size>=1073741824)
    return number_format($size/1073741824,$digit)."Go";
  else if ($size>=1048576)
    return number_format($size/1048576,$digit)."Mo";
  else if ($size>=1024)
    return number_format($size/1024,$digit)."Ko";
  else
    return $size."o";
}

function can_modify($item) {
  return can_do(
    $item,
    array('pending')
  );
}

function can_archive($item) {
  return can_do(
    $item,
    array('refused', 'validated')
  );
}

function can_delete($item) {
  return can_do(
    $item,
    array('archived')
  );
}

function can_do($item, $status=array()) {
  return in_array($item['status'], $status);
}

/*
 * Generic Data/value helpers
 */
function vardump($data) {
  ob_start();
  var_dump($data);
  $data = ob_get_contents();
  ob_end_clean();
  return $data;
}

/**
 * Format a callable object for logging
 * @param  string|array $callable The callable object
 * @return string The callable object string representation
 */
function format_callable($callable) {
  if (is_string($callable))
    return $callable."()";
  if (is_array($callable))
    if (is_string($callable[0]))
      return $callable[0]."::".$callable[1]."()";
    elseif (is_object($callable[0]))
      return get_class($callable[0])."->".$callable[1]."()";
    else
      return "Unkown->".$callable[1]."()";
  // @phpstan-ignore-next-line
  return vardump($callable);
}

/*
 * Generic file/directory helpers
 */
function dump_file($file_path, $max_age=3600) {
  if (is_file($file_path)) {
    header('Content-Type: '.mime_content_type($file_path));
    $last_modified_time = filemtime($file_path);
    $etag = md5_file($file_path);
    header("Cache-Control: max-age=$max_age, must-revalidate");
    header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
    header("Etag: $etag");

    if (
      (
        isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
        @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time
      ) || (
        isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
        trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag
      )
    ) {
      header("HTTP/1.1 304 Not Modified");
      exit();
    }

    header('Pragma: public');
    header('Content-Length: ' . filesize($file_path));
    readfile($file_path);
    exit();
  }
  header("HTTP/1.1 404 Not found");
  exit();
}

function delete_directory($dir, $recursive=true) {
  $files = array_diff(scandir($dir), array('.','..'));
  if ($recursive) {
    foreach ($files as $file) {
      if (is_dir("$dir/$file")) {
        if (!delete_directory("$dir/$file", true)) {
          Log :: error("delete_directory($dir) : Fail to delete sub-directory '$dir/$file'.");
          return false;
        }
      }
      else if (!unlink("$dir/$file")) {
        Log :: error("delete_directory($dir) : Fail to delete '$dir/$file'.");
        return false;
      }
    }
  }
  else if (!empty($files)) {
    Log :: error("delete_directory($dir) : Directory is not empty.");
    return false;
  }
  return rmdir($dir);
}

/*
 * Run external command helper
 */
/**
 * Run external command
 *
 * @param $command string|array        The command. It's could be an array of the command with its
 *                                     arguments.
 * @param $data_stdin string|null      The command arguments (optional, default: null)
 * @param $escape_command_args boolean If true, the command will be escaped
 *                                     (optional, default: true)
 *
 * @return false|array An array of return code, stdout and stderr result or False in case of fatal
 *                     error
 **/
function run_external_command($command, $data_stdin=null, $escape_command_args=true) {
  if (is_array($command))
    $command = implode(' ', $command);
  if ($escape_command_args)
    $command = escapeshellcmd($command);
  Log :: debug("Run external command: '$command'");
  $descriptorspec = array(
    0 => array("pipe", "r"),  // stdin
    1 => array("pipe", "w"),  // stdout
    2 => array("pipe", "w"),  // stderr
  );
  $process = proc_open($command, $descriptorspec, $pipes);

  if (!is_resource($process)) {
    Log :: error("Fail to run external command: '$command'");
    return false;
  }

  if (!is_null($data_stdin)) {
    fwrite($pipes[0], $data_stdin);
  }
  fclose($pipes[0]);

  $stdout = stream_get_contents($pipes[1]);
  fclose($pipes[1]);

  $stderr = stream_get_contents($pipes[2]);
  fclose($pipes[2]);

  $return_value = proc_close($process);

  $error = (!empty($stderr) || $return_value != 0);
  Log :: log(
    ($error?'ERROR':'DEBUG'),
    "External command ".($error?"error":"result").":\n".
    "\tCommand : $command\n".
    "\tReturn code: $return_value\n".
    "\tOutput:\n".
    "\t\t- Stdout :\n$stdout\n\n".
    "\t\t- Stderr :\n$stderr"
  );

  return array($return_value, $stdout, $stderr);
}

/**
 * Check an AJAX request and trigger a fatal error on fail
 *
 * Check if session key is present and valid and set AJAX
 * mode.
 *
 * @param string|null $session_key string The current session key (optional)
 *
 * @return void
 **/
function check_ajax_request($session_key=null) {
  global $ajax, $debug_ajax;
  Url :: api_mode(true);

  if (Session :: check_key($session_key))
    Tpl :: fatal_error('Invalid request');

  if (Tpl :: debug_ajax())
    Log :: debug("Ajax Request : ".vardump($_REQUEST));
}

# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab