<?php
class Astrolabe_Model extends CI_Model
{

    var $table = 'astrolabeprofile';

    function __construct()
    {
        parent::__construct();
    }

    function profile_id($profile)
    {

        $defaultTree = (array_key_exists('defaulttree', $profile)) ? $profile['defaulttree'] : false;
        $sharedTree = (array_key_exists('sharedTree', $profile)) ? $profile['sharedTree'] : false;
        $sharedFrom = (array_key_exists('sharedFrom', $profile)) ? $profile['sharedFrom'] : '';
        $globalTree = (array_key_exists('globaltree', $profile)) ? $profile['globaltree'] : false;
        $id = $profile['id'];
        $name = $profile['profileid'];
        return array(
            'id' => $id,
            'name' => $name,
            'defaultTree' => $defaultTree,
            'shared' => $sharedTree,
            'sharedFrom' => $sharedFrom,
            'global' => $globalTree
        );
    }

    function _map_for_insert($meta,$returnDefaults=false)
    {

        $sharedPermission = !empty($meta['sharedPermission']) && is_array($meta['sharedPermission']) ? $meta['sharedPermission'] : array();
        $sharedBy = !empty($meta['sharedBy']) && is_array($meta['sharedBy']) ? $meta['sharedBy'] : array();

        $mappedData['defaulttree'] = !empty($meta['defaulttree']) ? 'true' : 'false';
        $mappedData['globaltree'] = !empty($meta['globaltree']) ? 'true' : 'false';
        $mappedData['profileId'] = !empty($meta['profileId']) ? $meta['profileId'] : '';
        $mappedData['sharedPermission'] = '{' . implode(',', $sharedPermission) . '}';
        $mappedData['sharedBy'] = '{' . implode(',', $sharedBy) . '}';
        $mappedData['data'] = !empty($meta['data']) ? json_encode($meta['data']) : '{}';
        // only return specified key in meta array
        $returnArray = array();

        if ($returnDefaults)
        {
            $returnArray = $mappedData;
        }
        foreach ((array)$meta as $index => $data)
        {
            $returnArray[$index] = $mappedData[$index];
        }
        return $returnArray;
    }

    function _get_default_profiles($username)
    {
        $query = $this->db->get_where($this->table, array(
            'username' => $username
        ));
        $data = $query->result_array();

        $profiles = array_map(array(
            $this,
            'profile_id'
        ) , $data);

        return $profiles;
    }

    function _get_shared_profiles($username, $roles)
    {

        $sql = 'SELECT  * from astrolabeprofile
                WHERE ? && sharedBy
                OR ? && sharedPermission
                AND globaltree = FALSE
                AND username != ?';

        $arrayUsername = '{' . $username . '}';
        $arrayRoles = '{' . implode(',', $roles) . '}';

        $query = $this->db->query($sql, array(
            $arrayUsername,
            $arrayRoles,
            $username
        ));

        $data = $query->result_array();


        foreach ((array)$data as $index => $result)
        {
            $data[$index]['sharedTree'] = true;
            $data[$index]['sharedFrom'] = $result['username'];
            $data[$index]['defaulttree'] = true;
        }

        $profiles = array_map(array(
            $this,
            'profile_id'
        ) , $data);
        return $profiles;
    }

    function _get_global_profiles($username, $roles = array())
    {
        $arrayRoles = '{' . implode(',', $roles) . '}';
        $sql = 'SELECT * from astrolabeprofile WHERE  username <> ? and globalTree = TRUE and ? && sharedPermission';
        $query = $this->db->query($sql, array(
            $username,
            $arrayRoles
        ));
        $data = $query->result_array();


        foreach ((array)$data as $index => $result)
        {
            $data[$index]['sharedTree'] = true;
            $data[$index]['sharedFrom'] = $result['username'];
            $data[$index]['defaulttree'] = true;
        }

        $profiles = array_map(array(
            $this,
            'profile_id'
        ) , $data);
        return $profiles;
    }

    function profile_list($username, $roles = array())
    {
        if (is_null($username))
        {
            return NULL;
        }

        $profiles = $this->_get_default_profiles($username);
        $sharedProfile = $this->_get_shared_profiles($username, $roles);
        $globalProfile = $this->_get_global_profiles($username, $roles);

        $allProfiles =  array_merge($globalProfile, $sharedProfile, $profiles);


        return $allProfiles;
    }

    public function profile_get($username, $id, $shared = false, $sharedFrom = null, $roles = '{}')
    {
        if (!is_null($username) && !is_null($id))
        {

            if ($shared && !is_null($sharedFrom))
            {
                $sql = 'SELECT id,username,profileid as "profileId",defaulttree as default,globaltree as "globalTree",array_to_json(sharedpermission) as "sharedPermission",array_to_json(sharedby) as "sharedBy",data from astrolabeprofile
                        WHERE (id=? and username=?) AND (? && sharedBy OR ? && sharedpermission OR globalTree = TRUE)';
                $query = $this->db->query($sql, array(
                    $id,
                    $sharedFrom,
                    '{' . $username . '}',
                    $roles
                ));
                $result = $query->first_row();
            }
            else
            {
                $sql = 'SELECT id,username,profileid as "profileId",defaulttree as default,globaltree as "globalTree",array_to_json(sharedpermission) as "sharedPermission",array_to_json(sharedby) as "sharedBy",data from astrolabeprofile
                        WHERE id=? AND ( username =? OR ? && sharedBy OR ? && sharedpermission OR globalTree = TRUE )';

                /** 
                 * Return profile if ID equals the requested value
                 * and one of the conditions is true:
                 *  - user who created the profile equals the requested username
                 *  - profile is shared with the requested username
                 *  - profile is shared with one of the requested roles
                 *  - profile is global: globalTree=TRUE
                 **/
                $query = $this->db->query($sql, [
                    $id,
                    $username,
                    '{' . $username . '}',
                    $roles
                ]);
                $result =  $query->first_row();
            }
        }

        if (!empty($result))
        {
            // convert json to array
            $result->sharedPermission = json_decode($result->sharedPermission,true);
            $result->sharedBy = json_decode($result->sharedBy,true);
            $result->data = json_decode($result->data,true);
        }

        return $result;


    }

    function profile_insert($username, $profileId, $nodeDescriptionList, $meta = array())
    {
        if (is_null($username) || is_null($profileId) && is_null($nodeDescriptionList))
        {
            return false;
        }


        $insertArray = $this->_map_for_insert($meta,$returnDefaults = true);
        $sql = 'INSERT INTO astrolabeprofile (username, profileid, defaultTree, sharedPermission, sharedBy, globalTree, data)
                VALUES (?, ?, ?, ?, ?, ?, ?)';

        $result = $this->db->query($sql, array(
            $username,
            $profileId,
            $insertArray['defaulttree'],
            $insertArray['sharedPermission'],
            $insertArray['sharedBy'],
            $insertArray['globaltree'],
            json_encode($nodeDescriptionList)
        ));

        if (!$result)
        {
            // if query returns null
            $msg = $this->db->_error_message();
            $num = $this->db->_error_number();
            throw new Exception($msg, $num);
        }

        return $this->db->insert_id();
    }

    function add_builtin_profiles($username)
    {
        $meta = array('defaulttree' => false);
        return $this->profile_insert($username, 'OS', array(
            array(
                'label' => 'Linux',
                'classRegex' => 'linux',
                'children' => array(
                    array(
                        'label' => 'CentOS',
                        'classRegex' => 'centos',
                        'children' => array()
                    ) ,
                    array(
                        'label' => 'Red Hat',
                        'classRegex' => 'redhat_pure',
                        'children' => array()
                    ) ,
                    array(
                        'label' => 'Debian',
                        'classRegex' => 'debian_pure',
                        'children' => array()
                    ) ,
                    array(
                        'label' => 'SUSE',
                        'classRegex' => 'SUSE|SuSE',
                        'children' => array()
                    ) ,
                    array(
                        'label' => 'Ubuntu',
                        'classRegex' => 'ubuntu',
                        'children' => array()
                    )
                )
            ) ,
            array(
                'label' => 'Solaris',
                'classRegex' => 'solaris.*',
                'children' => array()
            ) ,
            array(
                'label' => 'HP-UX',
                'classRegex' => 'hpux',
                'children' => array()
            ) ,
            array(
                'label' => 'AIX',
                'classRegex' => 'aix',
                'children' => array()
            ) ,
            array(
                'label' => 'Windows',
                'classRegex' => 'windows',
                'children' => array()
            )
        ) , $meta);
    }

    function profile_delete($username, $id)
    {
        if (is_null($username) || is_null($id))
        {
            return false;
        }

        return $this->db->delete($this->table, array(
            'username' => $username,
            'id' => $id
        ));
    }

    function profile_delete_all($username)
    {
        log_message('debug', 'Deleting all profile of ' . $username);
        if (is_null($username))
        {
            return false;
        }

        return $this->db->delete($this->table, array(
            'username' => $username
        ));
    }

    function profile_update($username, $id, $newData = array())
    {
        if (is_null($username) || is_null($id) || empty($newData))
        {
            return false;
        }

        $updateArray = $this->_map_for_insert($newData);

        $result = $this->db->update($this->table, $updateArray, array(
            'username' => $username,
            'id' => $id
        ));
        return $result;
    }

    function _shared_profile($profiles, $username)
    {
        $result = array();


        foreach ((array)$profiles as $profile)
        {
            $defaultTree = (array_key_exists('defaulttree', $profile)) ? $profile['defaulttree'] : false;
            $sharedPermission = (array_key_exists('sharedPermission', $profile)) ? json_decode($profile['sharedPermission'],true) : array();
            $sharedBy = (array_key_exists('sharedBy', $profile)) ? json_decode($profile['sharedBy'],true) : array();
            $id = $profile['id'];
            $name = $profile['profileid'];
            $sharedTree = !empty($profile['sharedBy']) && in_array($username, $sharedBy); // already shared or not
            $result[] = array(
                'id' => $id,
                'username' => $profile['username'],
                'name' => $name,
                'defaultTree' => $defaultTree,
                'shared' => $sharedTree,
                'sharedPermission' => $sharedPermission,
                'sharedBy' => $sharedBy
            );
        }
        return $result;
    }
    /**
     * Get the list of subscription available to user.
     * @param type $username
     * @param type $roles
     * @return array
     */
    function subscription_get($username, $roles = array())
    {

        $sql = 'SELECT  id,username,profileid,defaulttree,globaltree,array_to_json(sharedpermission) AS "sharedPermission",
                        array_to_json(sharedby) AS "sharedBy",data
                FROM astrolabeprofile
                WHERE ? && sharedPermission AND username <> ? AND globaltree = FALSE';

        $arrayRoles = '{' . implode(',', $roles) . '}';
        $bind = array(
            $arrayRoles,
            $username
        );
        $query = $this->db->query($sql, $bind);
        $data = $query->result_array();


        $profiles = $this->_shared_profile($data, $username);
        return $profiles;
    }
    /**
     * add new subscription
     * @param type $username
     * @param type $id
     * @return boolean
     */
    function subscription_put($username, $roles, $id)
    {

        if (is_null($username) || is_null($id) || empty($roles))
        {
            return false;
        }

        $updateData = array(
            'sharedBy' => $username
        );

        $arrayRoles = '{' . implode(',', $roles) . '}';


        $updateSql = sprintf("UPDATE astrolabeprofile SET sharedBy = sharedBy || array['%s']::varchar[]
                     WHERE id = ? AND ? && sharedPermission AND not (sharedBy @> array['%s']::varchar[])", $this->db->escape_str($username), $this->db->escape_str($username));


        return $this->db->query($updateSql,array($id,$arrayRoles));
    }
    /**
     * add new subscription
     * @param type $username
     * @param type $id
     * @return boolean
     */
    function subscription_delete($username, $id)
    {

        if (is_null($username) || is_null($id))
        {
            return false;
        }

        $updateData = array(
            'sharedBy' => $username
        );

        $sql = 'UPDATE astrolabeprofile SET sharedBy = array_remove(sharedBy,?) where id=?';
        return $this->db->query($sql,array($username,$id));
    }
    /**
     * This copies the given tree with id in current user as local tree.
     * @param string $username
     * @param string $id
     * @return array true on success
     */
    function copy_tree($username, $from, $id)
    {
        $result = array();
        $originalProfile = $this->profile_get($from, $id);
        if (!empty($originalProfile))
        {
            $newName = $this->_generateNameForCopy($username, $originalProfile->profileId);
            $meta = array();
            $meta['sharedBy'] = array();
            $meta['sharedPermission'] = array();
            $meta['globaltree'] = false;
            $meta['defaulttree'] = false;
            $newId = $this->profile_insert($username, $newName, $originalProfile->data, $meta);
            $result['id'] = $newId;
            $result['name'] = $newName;
            return $result;
        }
        return false;
    }
    /**
     * Generates name for copying
     * Eg Copy_of_name
     * @param type $username
     * @param type $profileName
     * @return string
     */
    function _generateNameForCopy($username, $profileName)
    {
        $count = 0;


        do
        {
            $newname = 'Copy_of_' . $profileName;
            if ($count > 0)
            {
                $newname = 'Copy_of_' . $profileName . "_" . $count;
            }
            $count++;
        } while ($this->_checkForDuplicateName($username, $newname));

        return $newname;
    }
    /**
     *  return true if duplicate name is found
     * @param type $username
     * @param type $newname
     * @return boolean
     */
    function _checkForDuplicateName($username, $newname)
    {
        $query = $this->db->get_where($this->table,array(
            'username' => $username,
            'profileid' => $newname
        ),1);

        $data = $query->first_row();
        if (!empty($data))
        {
            return true;
        }

        return false;
    }
}
?>
