<?php

use EesyPHP\App;
use EesyPHP\Db;
use EesyPHP\Hook;
use EesyPHP\Log;

use Unidecode\Unidecode;

$db = new Db(
  App::get('db.dsn', null, 'string'),
  App::get('db.user', null, 'string'),
  App::get('db.password', null, 'string'),
  App::get('db.options', array(), 'array'),
  App::get('db.date_format', null, 'string'),
  App::get('db.datetime_format', null, 'string'),
);

/*
 * Methods to handle items
 */
function get_items($orderby='id', $raw_values=false) {
  global $db;
  try {
    $info = $db -> get_many('item', null, null, $orderby);
    if (!is_array($info))
      return;
    if ($raw_values)
      return $info;

    $items = array();
    foreach ($info as $item)
      $items[$item['id']] = $db -> format_row_info($item, array('date'));
    return $items;
  }
  catch (Exception $e) {
    Log :: error("Error retreiving items info from database : ".$e->getMessage());
  }
  return false;
}

function get_item($id, $raw_values=false) {
  global $db;
  try {
    $info = $db -> get_one('item', array('id' => $id));

    if (!is_array($info))
      return false;

    if ($raw_values)
      return $info;

    return $db -> format_row_info($info, array('date'));
  }
  catch (Exception $e) {
    Log :: error("Error retreiving item #$id info from database : ".$e->getMessage());
  }
  return false;
}

function add_item($values) {
  global $db;
  $values['date'] = $db -> time2datetime(time());
  try {
    $result = $db -> fpdo -> insertInto('item')
      -> values($values)
      -> execute();

    if ($result !== false) {
      $item = get_item($result);
      Log :: info("New item #$result added");
      Hook :: trigger('item_added', $item);
      return $item;
    }
  }
  catch (Exception $e) {
    Log :: error("Error creating item in database : ".$e->getMessage());
  }
  return false;
}

function update_item($id, $changes) {
  global $db;

  if (!is_array($changes))
    return false;
  if (empty($changes))
    return true;
  $item = get_item($id, true);
  if (!is_array($item)) return false;

  if (isset($changes['date']) && $changes['date'])
    $changes['date'] = $db -> time2datetime($changes['date']);

  try {
    $result = $db -> fpdo -> update('item')
      -> set($changes)
      -> where('id', $id)
      -> execute();

    if ($result !== false) {
      Log :: info("Item #$id updated");
      Hook :: trigger('item_updated', $item);
      return true;
    }
  }
  catch (Exception $e) {
    Log :: error("Error updating item #$id in database : ".$e->getMessage());
  }
  return false;
}

function change_item_status($id, $status) {
  if (update_item($id, array('status' => $status))) {
    Log :: info("Status of item #$id changed to $status.");
    return true;
  }
  return false;
}

function archive_item($id) {
  return change_item_status($id, 'archived');
}

function delete_item($id) {
  global $db;
  try {
    $result = $db -> fpdo -> deleteFrom('item')
      -> where('id', $id)
      -> execute();

    if ($result !== false) {
      Log :: info("Item #$id deleted");
      return True;
    }
  }
  catch (Exception $e) {
    Log :: error("Error deleting item #$id from database : ".$e->getMessage());
  }
  return false;
}

function search_items($params) {
  global $db;

  // Detect PgSQL backend
  $is_pgsql = (strpos(App::get('db.dsn', '', 'string'), "pgsql:") === 0);

  $where = array();
  if (isset($params['status']) && $params['status'] && $params['status'] != 'all')
     $where['status'] = $params['status'];

  $patterns_where = array();
  if (isset($params['pattern']) && $params['pattern']) {
    foreach(preg_split('/\s+/', trim($params['pattern'])) as $word) {
      if (!$word) continue;
      $patterns_word=array();

      // If numeric pattern ?
      if (preg_match('/^[0-9]+$/', $word)) {
        // Numeric pattern
        $word = intval($word);
        $patterns_word['id = ?'] = $word;
      }
      else {
        // Text pattern
        foreach (array('name', 'description') as $field) {
          if ($is_pgsql) {
            $word = Unidecode::unidecode($word);
            $patterns_word["unaccent($field) ILIKE ?"] = "%$word%";
          }
          else
            $patterns_word["$field LIKE ?"] = "%$word%";
        }
      }
      $patterns_where[] = $patterns_word;
    }
  }

  $order='id';
  $orders=array('id', 'name', 'date', 'status', 'description');
  if (isset($params['order'])) {
     if (!in_array($order, $orders))
        return -1;
     $order=$params['order'];
  }

  $order_direction='DESC';
  if (isset($params['order_direction']) && $params['order_direction']) {
     if (!in_array($params['order_direction'], array('ASC', 'DESC')))
        return -1;
     $order_direction=$params['order_direction'];
  }

  $orderby="$order $order_direction";

  $limit = "";
  $page = 1;
  $nb_by_page = 10;
  $offset = 0;
  if (!isset($params['all'])) {
     if (isset($params['page']) && $params['page']>0) {
        if (isset($params['nb_by_page']) && $params['nb_by_page']>0) {
           $nb_by_page = intval($params['nb_by_page']);
        }
        $page = intval($params['page']);
     }
     $offset = ($page-1)*$nb_by_page;
     $limit = $nb_by_page;
  }

  try {
    $query = $db -> fpdo -> from('item');
    if (!empty($where))
      $query -> where($where);
    foreach ($patterns_where as $patterns_word)
      call_user_func_array(
        array($query, 'where'),
        array_merge(
          array('('.implode(' OR ', array_keys($patterns_word)).')'),
          array_values($patterns_word)
        )
      );
    $result = $query  -> orderBy($orderby)
          -> limit($limit)
          -> offset($offset)
          -> execute();

    if ($result === false) {
      Log :: error('search_items() : search in DB return false');
      return false;
    }

    $rows = $result -> fetchAll();
    $items = array();
    foreach ($rows as $row) {
      $items[] = $db -> format_row_info($row, array('date'));
    }
    if (isset($params['all'])) {
      return array(
        'count'    => count($items),
        'first'    => 1,
        'last'     => count($items),
        'nb_pages' => 1,
        'page'     => 1,
        'items'    => $items
      );
    }
    $query_count = $db -> fpdo -> from('item')
      -> select(null)
      -> select('count(*) as count');
    if (!empty($where))
      $query_count -> where($where);
      foreach ($patterns_where as $patterns_word)
        call_user_func_array(
          array($query_count, 'where'),
          array_merge(
            array('('.implode(' OR ', array_keys($patterns_word)).')'),
            array_values($patterns_word)
          )
        );

    $result_count = $query_count -> execute();

    if ($result_count === false) {
      Log :: debug('search_items() : search for count in DB return false');
      return False;
    }
    $count = $result_count -> fetch();
    return array(
      'count'     => $count['count'],
      'first'     => $offset+1,
      'last'      => (
        $offset+$nb_by_page<$count['count']?
        $offset+$nb_by_page:$count['count']),
      'nb_pages'  => ceil($count['count']/$nb_by_page),
      'page'      => $page,
      'items'     => $items,
    );
  }
  catch (Exception $e) {
    Log :: exception(
      $e, "An exception occured searching items with params %s infos from database : ",
      preg_replace("/\n[ \t]*/", " ", print_r($params, true))
    );
  }
  return false;
}

function export_items($fd=null) {
  if (!$fd) $fd = fopen('php://output', 'w');
  fputcsv(
    $fd,
    array (
      'id',
      'name',
      'date',
      'status',
      'description',
    )
  );
  $items = get_items();
  foreach($items as $item) {
    fputcsv(
      $fd,
      array(
        $item['id'],
        $item['name'],
        $item['date'],
        $item['status'],
        $item['description'],
      )
    );
  }
  return True;
}

function restore_items($fd=null) {
  global $db;
  if (!$fd) $fd = fopen('php://stdin', 'r');
  try {
    $result = $db -> fpdo -> deleteFrom('item')
      -> execute();
    if ($result === false) {
      Log :: error("An unknown error occured truncating item table in database.");
      return false;
    }
  }
  catch (Exception $e) {
    Log :: error("Error truncating item table in database : ".$e->getMessage());
    return false;
  }

  $first_row = false;
  $line = 0;
  $restored = 0;
  $error = false;
  $datetime_fields = array (
    'date',
  );
  // Map fields to support hold CSV files format
  $mapping = array (
    'creation_date'  => 'date',
    'desc'           => 'description',
  );

  while (($row = fgetcsv($fd)) !== FALSE) {
    $line++;
    if ($first_row === false) {
      $first_row = $row;
      continue;
    }
    try {
      $values = array();
      for ($i=0; $i < count($first_row); $i++) {
        if (!$row[$i]) continue;
        $field = (
          array_key_exists($first_row[$i], $mapping)?
          $mapping[$first_row[$i]]:
          $first_row[$i]);
        $value = (in_array($field, $datetime_fields)?$db -> time2datetime($row[$i]):$row[$i]);
        $values[$field] = $value;
      }
      $result = $db -> fpdo -> insertInto('item')
        -> values($values)
        -> execute();

      if ($result !== false) {
        $restored++;
      }
      else {
        Log :: error("Unkwown error occured restoring item from line #$line :\n".print_r($values, true));
        $error = true;
      }
    }
    catch (Exception $e) {
      Log :: error(
        "Error restoring item from line #$line : ".$e->getMessage()."\n".print_r($values, true));
      $error = true;
    }
  }
  Log :: info("$restored items restored");

  // Trigger hooks
  Hook :: trigger('items_restored');

  return !$error;
}

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