<?php
/*******************************************************************************

    Copyright 2013 Whole Foods Co-op

    This file is part of CORE-POS.

    CORE-POS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    CORE-POS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    in the file license.txt along with IT CORE; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*********************************************************************************/

class AutoParsTask extends FannieTask
{

    public $name = 'Auto Pars Task';

    public $description = '';

    public $default_schedule = array(
        'min' => 15,
        'hour' => 3,
        'day' => '*',
        'month' => '*',
        'weekday' => '*',
    );

    public function run()
    {
        global $FANNIE_OP_DB, $FANNIE_TRANS_DB;
        $dbc = FannieDB::get($FANNIE_OP_DB);

        // look up item daily movement for last quarter 
        $salesQ = 'SELECT ' 
                . DTrans::sumQuantity('d') . ' AS qty, '
                . $dbc->datediff($dbc->now(), 'MIN(tdate)') . ' AS diff,
                  MAX(discounttype) AS onSale
                FROM ' . $FANNIE_TRANS_DB . $dbc->sep() . 'dlog_90_view AS d
                WHERE d.upc=?
                    AND d.store_id=?
                    AND charflag <> \'SO\'
                    AND trans_status <> \'R\'
                    AND quantity BETWEEN -1000 AND 1000
                GROUP BY year(tdate), month(tdate), day(tdate)
                ORDER BY year(tdate), month(tdate), day(tdate) DESC';
        $salesP = $dbc->prepare($salesQ);
        $prodP = $dbc->prepare('UPDATE products SET auto_par=? WHERE upc=? AND store_id=?');

        $date = date('Y-m-d', strtotime('1 month ago'));
        $lookupP = $dbc->prepare('
            SELECT upc,store_id
            FROM products
            WHERE (inUse=1 OR last_sold >= ?)
        ');
        $prodR = $dbc->execute($lookupP, array($date));
        $lambda = 0.25;
        // average daily sales for items at retail price
        // sale days are discarded from both quantity sold
        // and number of days
        while ($prodW = $dbc->fetchRow($prodR)) {
            $upc = $prodW['upc'];
            $store = $prodW['store_id'];
            $salesR = $dbc->execute($salesP, array($upc, $store));
            if ($dbc->numRows($salesR) == 0) {
                $dbc->execute($prodP, array(0, $upc, $store));
                if ($this->test_mode) {
                    break;
                } else {
                    continue;
                }
            }
            $avg = $this->getAverage($dbc, $salesR);
            $dbc->execute($prodP, array($avg, $upc, $store));
        }

        $salesQ = 'SELECT ' 
                . DTrans::sumQuantity('d') . ' AS qty, '
                . $dbc->datediff($dbc->now(), 'MIN(tdate)') . ' AS diff,
                  MAX(discounttype) AS onSale
                FROM ' . $FANNIE_TRANS_DB . $dbc->sep() . 'dlog_90_view AS d
                    INNER JOIN upcLike AS u ON d.upc=u.upc
                WHERE u.likeCode=?
                    AND d.store_id=?
                    AND charflag <> \'SO\'
                    AND trans_status <> \'R\'
                    AND quantity BETWEEN -1000 AND 1000
                GROUP BY year(tdate), month(tdate), day(tdate)
                ORDER BY year(tdate), month(tdate), day(tdate) DESC';
        $salesP = $dbc->prepare($salesQ);
        $prodR = $dbc->query("SELECT l.likeCode, s.storeID FROM likeCodes AS l, Stores AS s WHERE s.hasOwnItems=1");
        $model = new LikeCodeParsModel($dbc);
        while ($prodW = $dbc->fetchRow($prodR)) {
            $like = $prodW['likeCode'];
            $store = $prodW['storeID'];
            $salesR = $dbc->execute($salesP, array($like, $store));
            $model->likeCode($like);
            $model->storeID($store);
            if ($dbc->numRows($salesR) == 0) {
                $model->par(0);
                $model->save();
            } else {
                $avg = $this->getAverage($dbc, $salesR);
                $model->par($avg);
                $model->save();
            }
            if ($this->test_mode) {
                break;
            }
        }

        $zeroOldP = $dbc->prepare("UPDATE products SET auto_par = 0 where last_sold < NOW() - INTERVAL 180 DAY AND auto_par > 0;");
        $dbc->execute($zeroOldP);

    }

    private function getAverage($dbc, $salesR)
    {
        $max = 0;
        $days = array();
        $last_nonsale_qty = 0.1;
        $nonsale_qty = 0.1;
        $nonsale_count = 0;
        while ($salesW = $dbc->fetchRow($salesR)) {
            $index = $salesW['diff'];
            if ($index > $max) {
                $max = $index;
            }
            $days[$index] = $salesW['qty'];
            if ($salesW['onSale']) {
                $days[$index] = ($nonsale_count == 0 ? $nonsale_qty : $nonsale_qty/$nonsale_count);
            } else {
                $nonsale_qty += $salesW['qty'];
                $nonsale_count++;
            }
        }
        $sum = 0;
        $count = 0;
        for ($i=1; $i<=$max; $i++) {
            $sum += (isset($days[$i]) ? $days[$i] : 0);
            $count++;
        }
        $avg = ($count == 0) ? 0 : $sum/$count;

        return $avg;
    }

    // Box-Cox
    private function transform($val, $lambda)
    {
        if ($lambda == 0) {
            return log($val);
        } else {
            return (pow($val, $lambda) - 1) / $lambda;
        }
    }

    // Box-Cox
    private function backTransform($val, $lambda)
    {
        if ($lambda == 0) {
            return exp($val);
        } else {
            return pow(($val*$lambda)+1, (1/$lambda));
        }
    }
}

