uses "console", "sys", "array", "string", "structs.fe";

/*
 * The purpose of this file is to read in a ferite file and create a structure
 * of the ferite code
 *
 * The way it works is to look for c or C++style comments which start with *
 * for exmaple:
 */

/**
 * This is the C style
 */

//* This is the C++ style (except that this isn't supported)

/* So what is needed is a cunning set of regexp's to do the dirty work for me :) */

class ParserFile {
    string path;
    string module;
}

/**
 * @class Parser
 * @brief Implements a parser to process comments in a ferite source file
 */
class Parser
{
    
    /**
     * @variable file_list
     * @brief The list of files the parser will process
     * @type array
     */
    array  file_list;

    /**
     * @variable tree
     * @brief unknown
     * @type object
     */
    object tree;
    
    /**
     * @function start
     * @brief    Process the file list, and generate an object tree of the comments
     * @return   object
     * @declaration function start()
     */
    function start()
    {
        number i = 0;
        
        .tree = new DocScript();
        for( i = 0; i < Array.size(.file_list); i++ )
        {
            // Add the return from processing the file to the tree
            .process( .file_list[i], .tree );
        }
        return .tree;
    }
    
    /**
     * @function addFile
     * @brief    Add a file to the list of files the parser will process
     * @return   void
     * @param    string filename The file to add to the list
     * @declaration function addFile( string filename )
     */
    function addFile( string filename, string module )
    {
        if( filename != "" )
        {
            object file = new ParserFile();
            file.path = filename;
            file.module = module;
            .file_list[] = file;
        }
    }
    
    /**
     * @function process
     * @brief    unknown
     * @return   object
     * @param    object file The script to process
     * @param    object parent The parent node to add this script to
     * @declaration function process( object file , object parent )
     */
    function process( object file, object parent )
    {
        array obj_stack;
        object tree = parent;
        object fileObj = File.open( file.path );
        
        if( fileObj != null )
        {
            string contents = fileObj.toString();
            object captureblocks = new Regexp( '/\*\*(.*?)\*/', 'gs' );
            object stripstuff = new Regexp( '^[\t ]*\*[ \t\n]*', 'gm' );
            object capturekeyvalue = new Regexp( '(@([a-zA-Z_-]+)( ([^@]*))?)', 'gs' );
            object stripnewline = new Regexp( "\n", 'gs' );

	    lastparserfile = file.path;
            Console.println( "Parsing file ${file.path}" );
            captureblocks.matchAll( contents ) using ( match ) 
            {
                string block = String.trim( stripstuff.replaceAll( match.capture(0), ' ' ), "\t\r" );
                array values = [];
                
                //Console.println( "block: $block" );
                
                values["module"] = file.module;
                capturekeyvalue.matchAll( block ) using ( match ) {
                    string token = match.capture(1);
                    string meaning = stripnewline.replaceAll( match.capture(3) );
                    
                //Console.println( "$token: $meaning" );
                    
                    if( values[token] isa void )  // The value doesn't exist
                        values[token] = meaning;
                    else
                    {
                        if( values[token] isa string )  // The value does exist, convert to an array
                        {
                            string temp = values[token];
                            Array.del( values, token );
                            values[token] = [temp, meaning];
                        }
                        else  // Add to the array
                            values[token][] = meaning;
                    }
                };
                
//                Console.println( "values: $values" );
                
                if( Array.keyExists( values, "end" ) )
                    tree = Array.pop(obj_stack);
                else
                {
                    object newObj = .generateObject( values );
//                    Console.println( "newObj: $newObj" );
                    {
                        object ret = tree.add( newObj );                    
                        if( Array.keyExists( values, "class" ) || 
                            Array.keyExists( values, "protocol" ) ||
                            Array.keyExists( values, "namespace" ) || 
                            Array.keyExists( values, "group" ) )
                        {
                            Array.push( obj_stack, tree );
                            if( newObj.mods != "" )
                                tree = ret;
                            else
                                tree = newObj;
                        }
                    }        
                }
            };
        }
        else
        {
            Console.println("Unable to open file ${file.path}");
        }
        if( Array.size(obj_stack) > 0 )
            tree = obj_stack[0];
        
        return tree;
    }
    
    /**
     * @function dump_array
     * @brief Dump the key/value pairs in the array
     * @declaration function dump_array( array v )
     * @param array v The array to dump
     * @return void
     */
    function dump_array( array v )
    {
        number i = 0;
        array keys = Array.keys(v);
        
        Console.println( "Begin Value Dump" );
        for( i = 0; i < Array.size(keys); i++ )
        {
            Console.println( "'" + keys[i] + "' = \"" + v[keys[i]] + "\"" );
        }
        Console.println( "" );
    }
    
    /**
     * @function generateGeneralObject
     * @brief    unknown
     * @return   object
     * @param    object obj unknown
     * @param    array values unknown
     * @declaration function generateGeneralObject( object obj , array values )
     */
    function generateGeneralObject( object obj, array values )
    {
        if( Array.keyExists( values, "module" ) )
            obj.module = values["module"];
        
        if( Array.keyExists( values, "declaration" ) )
            obj.declaration = values["declaration"];
        
        if( Array.keyExists( values, "brief" ) )
            obj.brief = values["brief"];
        
        if( Array.keyExists( values, "description" ) )
            obj.description = values["description"];
        
        if( Array.keyExists( values, "static" ) )
            obj.is_static = true;
        
        if( Array.keyExists( values, "warning" ) )
            obj.warning = values["warning"];

        if( Array.keyExists( values, "example" ) )
            obj.example = values["example"];

        return obj;
    }
    
    /**
     * @function generateFunctionObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateFunctionObject( array values )
     */
    function generateFunctionObject( array values )
    {
        object obj = .generateGeneralObject( new DocFunction(), values );
        object reg = new Regexp( ' ', 'g' );
        void params;
        number i = 0;
        
        obj.name = values["function"];
        obj.name = reg.replaceAll( obj.name, '' );
                        
        if( Array.keyExists( values, "return" ) )
            obj.return_type = values["return"];
        
        if( Array.keyExists( values, "param" ) )
        {
            params = values["param"];
            
            if( params isa array )
                obj.parameters = params;
            else
                obj.parameters[] = params;
        }
        else
            obj.parameters = [];
        
        return obj;
    }
    
    /**
     * @function generateVariableObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateVariableObject( array values )
     */
    function generateVariableObject( array values )
    {
        object obj = .generateGeneralObject( new DocVariable(), values );
        object reg = new Regexp( ' ', 'g' );
        number i = 0;
        
        obj.name = values["variable"];
        obj.name = reg.replaceAll( obj.name, '' );
        
        if( Array.keyExists( values, "type" ) )
            obj.type = values["type"];
        return obj;
    }
    
    /**
     * @function generateNamespaceObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateNamespaceObject( array values )
     */
    function generateNamespaceObject( array values )
    {
        object obj = .generateGeneralObject( new DocNamespace(), values );
        object reg = new Regexp( ' ', 'g' );
        number i = 0;
        
        obj.name = values["namespace"];
        obj.name = reg.replaceAll( obj.name, '' );
        
        if( Array.keyExists( values, "modifies" ) )
            obj.mods = values["modifies"];
        
        return obj;
    }
    
    /**
     * @function generateGroupObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateGroupObject( array values )
     */
    function generateGroupObject( array values )
    {
        object obj = .generateGeneralObject( new DocGroup(), values );
        number i = 0;
        
        obj.name = values["group"];
        
        if( Array.keyExists( values, "modifies" ) )
            obj.mods = values["modifies"];
        
        return obj;
    }
    
    /**
     * @function generateClassObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateClassObject( array values )
     */
    function generateClassObject( array values )
    {
        object obj = .generateGeneralObject( new DocClass(), values );
        object reg = new Regexp( ' ', 'g' );
        number i = 0;
        
        obj.name = values["class"];
        obj.name = reg.replaceAll( obj.name, '' );
        
        if( Array.keyExists( values, "modifies" ) )
            obj.mods = values["modifies"];
        if( Array.keyExists( values, "extends" ) )
            obj.exts = values["extends"];
        if( Array.keyExists( values, "implements" ) )
            obj.implements_list = values["implements"];
        
        return obj;
    }

    /**
     * @function generateProtocolObject
     * @brief    unknown
     * @return   object
     * @param    array values unknown
     * @declaration function generateProtocolObject( array values )
     */
    function generateProtocolObject( array values )
    {
        object obj = .generateGeneralObject( new DocProtocol(), values );
        object reg = new Regexp( ' ', 'g' );
        number i = 0;
        
        obj.name = values["protocol"];
        obj.name = reg.replaceAll( obj.name, '' );
        return obj;
    }

    /**
     * @function generateObject
     * @brief    Generate all of the documentation objects for the script.
     * @return   null
     * @param    array values unknown
     * @declaration function generateObject( array values )
     */
    function generateObject( array values )
    {
        /* our first value gives out what sort of object we should be creating */
        if( Array.keyExists( values, "function" ) )
            return .generateFunctionObject( values );
        if( Array.keyExists( values, "variable" ) )
            return .generateVariableObject( values );
        if( Array.keyExists( values, "class" ) )
            return .generateClassObject( values );
        if( Array.keyExists( values, "namespace" ) )
            return .generateNamespaceObject( values );
        if( Array.keyExists( values, "group" ) )
            return .generateGroupObject( values );
        if( Array.keyExists( values, "protocol" ) )
            return .generateProtocolObject( values );
        return null;
    }
}
/**
 * @end
 */
