<?php

/**
 * The definition for the contact_resultset class.
 *
 * @package CGContacts
 * @category Utilities
 * @author  calguy1000 <calguy1000@cmsmadesimple.org>
 * @copyright Copyright 2014 by Robert Campbell
 */

namespace CGContacts;

/**
 * This class represents the results of a query from contact_query.
 */
final class contact_resultset extends \CGExtensions\query\resultset
{
    /**
     * @ignore
     */
    private $_preloaded_cats;

    /**
     * @ignore
     */
    private $_preloaded_objects;

    /**
     * @ignore
     */
    private $_preloaded_contacts;

    /**
     * Constructor.
     *
     * @param contact_query $query
     */
    public function __construct(contact_query $query)
    {
        $this->_filter = $query;
        $this->_query();
    }

    /**
     * Get the query used to construct this object.
     *
     * @return contact_query
     */
    public function &get_query()
    {
        return $this->_filter;
    }

    /**
     * @ignore
     */
    protected function _query()
    {
        if( $this->_rs ) return;

        $query = 'SELECT SQL_CALC_FOUND_ROWS %cols% FROM '.CGCONTACTS_TBL.' C ';
        $cols = array('C.id');
        $where = array();
        $joins = array();
        $parms = array();
        $uselike = \cge_param::get_bool($this->_filter,'userlike');

        if( ($idlist = $this->_filter['idlist']) ) {
            $str = '('.implode(',',$idlist).')';
            $where[] = 'C.id IN '.$str;
        }

        if( $this->_filter['name'] ) {
            // name automatically uses like
            $str = trim($this->_filter['name']);
            if( strlen($str) > 1 ) {
                // minimum 2 characters, can now use like expression.
                $tmp = array();
                $str = "%{$str}%";
                $tmp[] = 'C.fullname LIKE ?';
                $tmp[] = 'C.nickname LIKE ?';
                $joins['name'] = CGCONTACTS_SUB_TBL.' SU ON C.id = SU.contact_id AND SU.class = \'contact_name\'';
                $tmp[] = 'SU.value like ?';
                $where[] = '('.implode(' OR ',$tmp).')';
                $parms[] = $str;
                $parms[] = $str;
                $parms[] = $str;
            }
        }

        if( ($val = $this->_filter['feu_uid']) ) {
            $where[] = 'C.feu_uid = ?';
            $parms[] = $val;
        }

        if( ($val = $this->_filter['fullname']) ) {
            if( $uselike ) {
                $where[] = 'C.fullname LIKE ?';
                $parms[] = $this->wildcard($val);
            }
            else {
                $where[] = 'C.fullname = ?';
                $parms[] = $val;
            }
        }

        if( ($val = $this->_filter['modified_since']) ) {
            $where[] = 'C.modified >= ?';
            $parms[] = $val;
        }

        if( $this->_filter['category'] ) {
            $category_id_list = array();
            $category = $this->_filter['category'];
            if( !is_array($category) ) $category = explode(',',$category);

            foreach( $category as $onecat ) {
                // convert the string to a category id.
                $id = null;
                if( is_numeric($onecat) && (int) $onecat > 0 ) {
                    $id = $onecat;
                }
                else {
                    $id = category::get_category_id($onecat);
                }
                if( !$id ) continue;

                if( !$this->_filter['catchildren'] ) {
                    $category_id_list[] = $id;
                }
                else {
                    $tmp = category::expand_category($id);
                    if( is_array($tmp) && count($tmp) ) $category_id_list = array_merge($category_id_list,$tmp);
                }
            }

            // if we specified categories, but couldn't find any category ids
            // then we make sure that this item will match no categories
            if( !count($category_id_list) ) $category_id_list[] = -999;
            $cols[] = 'AC.cat_id AS cat_id';
            $joins['adr_cats'] = CGCONTACTS_ADR_CATS_TBL.' AC ON C.id = AC.contact_id';
            $where[] = 'AC.cat_id IN ('.implode(',',$category_id_list).')';
        }

        if( count($this->_filter['props']) ) {
            $props = $this->_filter['props'];
            for( $i = 0; $i < 50; $i++ ) {
                $key1 = 'propclass'.$i;
                $key2 = 'propval'.$i;
                $key3 = 'proplabel'.$i;
                $key = \cge_param::get_string($props,$key1);
                $val = \cge_param::get_string($props,$key2);
                $lbl = \cge_param::get_string($props,$key3);
                if( !$key && !$val ) break;

                $kkey = 'props'.$i;
                $joins[$kkey] = CGCONTACTS_SUB_TBL." $kkey ON C.id = $kkey.contact_id";
                if( $lbl ) {
                    $where[] = "$kkey.label = ?";
                    $parms[] = $lbl;
                }
                if( $uselike ) {
                    $where[] = "$kkey.value LIKE ?";
                    $parms[] = $this->wildcard($val);
                }
                else {
                    $where[] = "$kkey.value = ?";
                    $parms[] = $val;
                }
            }
        }

        //
        // BUILD THIS QUERY
        //

        if( count($joins) ) {
            $query .= ' LEFT JOIN '.implode(' LEFT JOIN ',$joins);
        }
        if( count($where) ) {
            $query .= ' WHERE '.implode(' AND ',$where);
        }

        switch( $this->_filter['sortby'] ) {
        case 'modified':
        case 'fullname':
        case 'nickname':
        case 'title':
        case 'company':
        case 'role':
            $query .= ' ORDER BY '.$this->_filter['sortby'];
            break;

        case 'display_name': // special value
            die('not done '.__FILE__.'::'.__LINE__);
        }

        switch( $this->_filter['sortorder'] ) {
        case 'ASC':
        case 'DESC':
            $query .= ' '.$this->_filter['sortorder'];
        }
        $query = str_replace('%cols%',implode(',',$cols),$query);

        // calculate indexes

        // now execute the thing
        $db = \cge_utils::get_db();
        $this->_rs = $db->SelectLimit($query,$this->_filter['limit'],$this->_filter['offset'],$parms);
        if( !$this->_rs ) throw new \Exception('INTERNAL ERROR: Query failed - '.$db->sql.' -- '.$db->ErrorMsg());
        $this->_totalmatching = (int) $db->GetOne('SELECT FOUND_ROWS()');

        $this->_preload();
    }

    /**
     * Get the number of records returned in this resultset.
     *
     * @return int
     */
    public function RecordCount()
    {
        $this->_query();
        if( $this->_rs ) return $this->_rs->RecordCount();
    }

    /**
     * Move to the next record in the resultset.
     */
    public function MoveNext()
    {
        $this->_query();
        if( $this->_rs ) return $this->_rs->MoveNext();
    }

    /**
     * Move to the first record in the resultset.
     */
    public function MoveFirst()
    {
        $this->_query();
        if( $this->_rs ) return $this->_rs->MoveFirst();
    }

    /**
     * Move to the first record in the resultset.
     */
    public function Rewind()
    {
        return $this->MoveFirst();
    }

    /**
     * Move to the last record in the resultset.
     */
    public function MoveLast()
    {
        $this->_query();
        if( $this->_rs ) return $this->_rs->MoveLast();
    }

    /**
     * Test if we are at the end of the resultset.
     *
     * @return bool
     */
    public function EOF()
    {
        $this->_query();
        if( $this->_rs ) return $this->_rs->EOF();
        return TRUE;
    }

    /**
     * Close the resultset.
     */
    public function Close()
    {
        if( $this->_rs ) return $this->_rs->Close();
    }

    /**
     * Return the total number of records that match the query (not only in this recordset).
     *
     * @return int
     */
    public function TotalMatches()
    {
        $this->_query();
        return $this->_totalmatching;
    }

    /**
     * @ignore
     */
    protected function _preload()
    {
        if( !$this->_rs ) return;

        $idlist = array();
        $this->_rs->MoveFirst();
        while( !$this->EOF() ) {
            $idlist[] = (int)$this->fields['id'];
            $this->MoveNext();
        }
        $this->_rs->MoveFirst();
        $idlist = array_unique($idlist);

        if( !count($idlist) ) return; // nothing to do.

        $db = \cge_utils::get_db();

        // preload the contact objects
        $query = 'SELECT id,status,guid,image,logo,fullname,nickname,company,title,role,note,modified FROM '.CGCONTACTS_TBL.' WHERE id IN('.implode(',',$idlist).') ORDER BY id';
        $contacts = $db->GetArray($query);
        if( !is_array($contacts) || count($contacts) == 0 ) return; // this is weird
        $this->_preloaded_contacts = \cge_array::to_hash($contacts,'id');

        // preload the objects
        $query = 'SELECT contact_id,preferred,class,label,value FROM '.CGCONTACTS_SUB_TBL.' WHERE contact_id IN ('.implode(',',$idlist).') ORDER BY contact_id, preferred, class, label';
        $objects = $db->GetArray($query);
        if( is_array($objects) && count($objects) ) $this->_preloaded_objects = $objects;

        // preload the categories
        $query = 'SELECT contact_id,cat_id FROM '.CGCONTACTS_ADR_CATS_TBL.' WHERE contact_id IN ('.implode(',',$idlist).') ORDER BY contact_id';
        $categories = $db->GetArray($query);
        if( is_array($categories) && count($categories)) $this->_preloaded_cats = $categories;
    }

    /**
     * Get the contact object for the current record.
     *
     * @return contact
     */
    public function &get_object()
    {
        $ret = null;
        if( !$this->_rs ) return $ret;

        $contact_id = $this->fields['id'];
        if( !isset($this->_preloaded_contacts[$contact_id]) ) {
            throw new \Exception('Internal error: somehow the contact info for this contact was not preloaded');
        }

        $contact = $this->_preloaded_contacts[$contact_id];

        // merge in the objects, if any
        for( $i = 0; $i < count($this->_preloaded_objects); $i++ ) {
            if( $this->_preloaded_objects[$i]['contact_id'] != $contact_id ) continue;
            if( !isset($contact['objects']) ) $contact['objects'] = array();

            $contact['objects'][] = $this->_preloaded_objects[$i];
        }

        // merge in the categories, if any
        for( $i = 0; $i < count($this->_preloaded_cats); $i++ ) {
            if( $this->_preloaded_cats[$i]['contact_id'] != $contact_id ) continue;
            if( !isset($contact['categories']) ) $contact['categories'] = array();

            $contact['categories'][] = $this->_preloaded_cats[$i];
        }

        $obj = new contact;
        $obj->from_data($contact);
        return $obj;
    }

    /**
     * Get the contact object for the current record.
     * This is a convenience wrapper for get_object.
     *
     * @return contact
     */
    public function &get_contact()
    {
        $tmp = $this->get_object();
        return $tmp;
    }

    /**
     * Get an object to aide in scrolling through the different pages of objects
     * that match this query.
     *
     * @return contact_pagination
     */
    public function &get_pagination()
    {
        $this->_query();
        $obj = new contact_pagination($this);
        return $obj;
    }
} // end of class

?>
