<?php
class FishLookup
{
    function &
get_me()
    {
        static 
$me null;
        if (
$me === null)
        {
            if (empty(
$this) or !is_a($this'FishLookup'))
                
$me = new FishLookup;
            else
                
$me =& $this;
        }
        return 
$me;
    }

    function 
printable($thing)
    {
        return 
preg_replace('/[[:space:]]+/'' 'var_export($thingTRUE));
    }

    function 
simple_add($current$new)
    {
        if (
$current === null)
        {
            
$current $new;
        }
        elseif (!
is_array($current) and $current != $new)
        {
            
$t $current;
            
$current = array();
            
$current[$t] = null;
            
$current[$new] = null;
        }
        elseif (!
array_key_exists($new$current))
        {
            
$current[$new] = null;
        }
        return 
$current;
    }

    function 
denull($current,$debug=false)
    {
        if (
is_array($current))
        {
            if (
current($current) === false)
                
reset($current);
            if (
current($current) === null)
            {
                if (
count($current) == 1)
                    
$current key($current);
                else
                    
$current array_keys($current);
            }
        }
        return 
$current;
    }

    function 
rowtreeme($rankfields$rows$debug=false)
    {
        static 
$tick 0;

        
$mytick $tick++;

        if (empty(
$rankfields))
        {
            return 
null;
        }

        if (empty(
$rows))
        {
            return 
null;
        }

        
$me =& FishLookup::get_me();

        if (!
is_array($rows))
        {
            return 
null;
        }

        
$this_rank array_shift($rankfields);
        if (
current($rows) === false)
            
reset($rows);
        
$left array_diff(array_keys(current($rows)), $this_rank);
        
$rowcount count(current($rows));
        
$fieldcount count($this_rank);
        if (empty(
$left))
        {
            
$results null;
            foreach (
$rows as $row)
            {
                if (
current($this_rank) === false)
                    
reset($this_rank);
                
$i key($this_rank);
                
$outerfield current($this_rank);
                
$outervalue $row[$outerfield];
                
$results $me->simple_add($results$outervalue);
                for (
$j $i 1$j $fieldcount$j++)
                {
                    
$innerfield $this_rank[$j];
                    
$innervalue $row[$innerfield];
                    if (
$results == $outervalue)
                        
$results = array($outervalue=>null);
                    
$results[$outervalue] = $me->simple_add($results[$outervalue], $innervalue);
                }
            }
            if (
is_array($results) and array_unique($results) == array(null))
                
$results array_keys($results);
            return 
$results;
        }
        
$uvalues = array();
        
$newrows $rows;
        foreach (
$newrows as $i => $row)
        {
            foreach (
$this_rank as $field)
            {
                if (empty(
$row[$field]))
                {
                    
user_error("lookup: $mytick: Field $field not in row"E_USER_WARNING);
                    continue;
                }
                
$v $row[$field];
                
$uvalues[$v][] = $i;
                unset(
$newrows[$i][$field]);
            }
        }
        
$results = array();
        foreach (
$uvalues as $uv => $is)
        {
            
$urows = array();
            foreach (
$is as $i)
                
$urows[] = $newrows[$i];
            
$output $me->rowtreeme($rankfields$urows$debug);
            if (
is_array($output))
            {
                foreach (
$output as $ok => $ov)
                    
$results[$uv][$ok] = $ov;
            }
            else
            {
                
$results[$uv] = $output;
            }
        }
        foreach (
array_keys($results) as $rk)
        {
            
$results[$rk] = $me->denull($results[$rk]);
        }
        
$ars array_filter($results'is_array');
        if (!empty(
$ars))
        {
            foreach (
array_keys($results) as $rk)
                if (!
is_array($results[$rk]))
                    
$results[$rk] = array($results[$rk]);
        }
        return 
$results;
    }

    function 
rankfields($new=null)
    {
        static 
$rankfields = array();
        if (
$new !== null)
            
$rankfields $new;
        return 
$rankfields;
    }

    function 
rows($new=null)
    {
        static 
$rows = array();
        if (
$new !== null)
            
$rows $new;
        return 
$rows;
    }

    function 
findme($values)
    {
        static 
$fieldranks null;

        
$me =& FishLookup::get_me();
        
$rankfields $me->rankfields();
        
$rows $me->rows();

        if (
$fieldranks === null)
        {
            foreach (
$rankfields as $rank => $fields)
            {
                foreach (
$fields as $field)
                    
$fieldranks[$field] = $rank;
            }
        }

        static 
$valuefields null;
        if (
$valuefields === null)
        {
            
$valuefields = array();
            foreach (
$rows as $i => $row)
                foreach (
$row as $f => $v)
                    
$valuefields[$v][$f][] = $i;
        }

        
$foundfields = array();
        
$foundvaluefields = array();
        
$foundrowis = array();
        
$foundrankfields = array();

        
$return_tree false;
        
$whole_row false;
        foreach (
$values as $x)
        {
            
$exclude false;
            if (
is_array($x))
            {
                
$combined = array();
                foreach (
$x as $xf => $xv)
                {
                    if (
strpos($xv,'!') === 0)
                    {
                        
$exclude true;
                        
$xv substr($xv,1);
                    }
                    if (empty(
$valuefields[$xv]))
                    {
                        if (
$exclude)
                            continue;
                        
user_error("lookup: Error = no value '$xv' found"E_USER_WARNING);
                        return 
FALSE;
                    }
                    if (
is_string($xf))
                    {
                        if (empty(
$valuefields[$xv][$xf]))
                        {
                            if (
$exclude)
                                continue;
                            
user_error("lookup: Error = no row with field '$xf' = '$xv' found"E_USER_WARNING);
                            return 
FALSE;
                        }
                        
$is $valuefields[$xv][$xf];
                        if (
$exclude)
                        {
                            if (!empty(
$foundrowis))
                                
$foundrowis array_diff($foundrowis$is);
                            continue;
                        }
                        
$foundvaluefields[$xf] = 1;
                        if (empty(
$foundrowis))
                            
$foundrowis $is;
                        else
                            
$foundrowis array_intersect($foundrowis$is);
                        continue;
                    }
                    else
                    {
                        
$combined[] = $xv;
                    }
                }
                if (!empty(
$combined))
                {
                    
$cis = array();
                    foreach (
$combined as $xv)
                    {
                        foreach (
$valuefields[$xv] as $xf => $is)
                        {
                            
$foundvaluefields[$xf] = 1;
                            foreach (
$is as $i)
                                
$cis[] = $i;
                        }
                    }
                    if (empty(
$foundrowis))
                        
$foundrowis $cis;
                    else
                        
$foundrowis array_intersect($foundrowis$cis);
                }
                continue;
            }
            if (
$x == 'row')
            {
                
$whole_row true;
                continue;
            }
            if (
$x == 'tree')
            {
                
$return_tree true;
                continue;
            }
            if (!
$whole_row and array_key_exists($x$fieldranks))
            {
                
$foundfields[] = $x;
                
$foundrankfields[$fieldranks[$x]][] = $x;
                continue;
            }
            if (
strpos($x,'!') === 0)
            {
                
$exclude true;
                
$x substr($x,1);
            }
            if (
array_key_exists($x$valuefields))
            {
                foreach (
$valuefields[$x] as $f => $is)
                {
                    if (
$exclude)
                    {
                        if (!empty(
$foundrowis))
                            
$foundrowis array_diff($foundrowis$is);
                        continue;
                    }
                    
$foundvaluefields[$f] = 1;
                    if (empty(
$foundrowis))
                        
$foundrowis $is;
                    else
                        
$foundrowis array_intersect($foundrowis$is);
                }
                continue;
            }
            
user_error("lookup: i have no idea what x=$x is"E_USER_WARNING);
        }

        if (empty(
$foundrowis))
        {
            if (
current($rows) === false)
                
reset($rows);
            
$foundrowis array_keys($rows);
            
$foundvaluefields array_flip(array_keys(current($rows)));
        }

        if (
$whole_row)
        {
            
// return all fields
            
$foundfields array_keys($fieldranks);
            
$foundrankfields $rankfields;
        }
        else
        {
            if (empty(
$foundfields))
            {
                
$foundfields array_keys($foundvaluefields);
                foreach (
$foundfields as $f)
                    
$foundrankfields[$fieldranks[$f]][] = $f;
            }
            
$foundrankfields array_values($foundrankfields);
        }

        if (empty(
$foundfields) and empty($foundrowis))
        {
            
user_error("lookup: Yo, nothing to look for"E_USER_WARNING);
            return 
FALSE;
        }

        
$foundrows = array();
        
$fieldcount count($foundfields);
        
$kfield null;
        
$vfield null;
        if (!
$return_tree)
        {
            if (
$fieldcount 3)
            {
                
$kfield array_shift($foundfields);
                if (
$fieldcount == 2)
                    
$vfield array_shift($foundfields);
                else
                    
$foundrows null;
            }
        }
        if (
count($foundrowis) > 1)
        {
            
sort($foundrowisSORT_NUMERIC);
            
reset($foundrowis);
        }
        foreach (
$foundrowis as $i)
        {
            if (
$kfield)
            {
                
$kvalue $rows[$i][$kfield];
                if (
$vfield)
                {
                    if (
$foundrows === null)
                        
$foundrows = array();
                    
$foundrows[$kvalue] = $rows[$i][$vfield];
                }
                else
                {
                    
$foundrows $me->simple_add($foundrows$kvalue);
                }
            }
            else
            {
                if (
$foundrows === null)
                    
$foundrows = array();
                
$row = array();
                foreach (
$foundfields as $f)
                    
$row[$f] = $rows[$i][$f];
                
$foundrows[] = $row;
            }
        }

        if (
$return_tree)
            
$foundrows $me->rowtreeme($foundrankfields$foundrows);

        return 
$foundrows;
    }
}
?>