<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
                       xmlns:s="library://ns.adobe.com/flex/spark" 
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="100%"
                       height="100%"
                       creationComplete="initApp();"
                       viewSourceURL="srcview/index.html" xmlns:local="*"
                       >
    <!--
    THE PURPOSE OF THIS PROGRAMM:
    
        TASKTRACER time a project management tool
        
        Time tracking and project management in your pocket.
        its a simple but effective way to track time and split up big problems in INFINITE(!) (UNLIMITED, NOT LIMITED LIKE M$ PRODUCTS) smaler tasks. FOR FUCKING FREE! you can download the source code by right clicking, developing it further and spreading it again. its under GPL v3 so please no commercialization. thanks.
        
        .... to split a big task/project into smaller tasks/projects
        and track how much time was needed to complete each sub-task.
        
        maybe later add some report generation/billing capabilities.
        
        it reminds you of what you need still need to do, let's you print reports to bill your boss/customers.
        
        at the same time it tries to stay as simple and well documented as possible. (if not tell me)
        
        to simplify things there are no "projects" anymore, everything is a task now.
        
        to simplify things even more, for each task and time there is only ONE comment.
        
        this comment is only updated.
        
        so the comment table is obsolete :)
        
    TEST DOCUMENTATION:

        o 1. add task
        o 2. check that adding task with the same name as previous task can not be done.
        o 3. update task
        o 4. delete task
        o 5. add subtask to task
        o 6. check that adding subtask with the same name as previous subtask can not be done.
        o 7. automatically select parent-tasks when selecting a subtask... and deselect all other tasks right of this task.
        o 7.1. after update/add of task or time the last selected task must again be selected again
        o 8. update subtask
        o 9. delete subtask
        o 10. add comment to task
        o 10.1. update/delete comment of task
        o 11. add comment to subtask
        o 12. add time to task
        o 13. add time to subtask
        ok 13.1. add comment to time
        o 14. update time of subtask
        o 15. delete time of subtask
        o 16. report how much time all subtasks of a task have taken... preferably as pdf/cvs->excel.
        o 17. check that the user can not add more than 1 empty datagrid/column
        o 18. add status to task
        o 19. add status to time
        == commercialization ==
        o xx. compile it as air/iphone app and sell this program for 1€ per download (while keeping it open source? ;)
        o xx. create a online pendant to this that syncs with the offline databases
        
    FEATURE REQUEST: (higher priority is on top with smallest index-number)
    
        ok -7. Adding a comment resets time counter!?
        ok -7.1. switching tasks resets time counter -> not good, counter stop, add save time before resetting
        
        ok -6. how much time spend on each task (tooltip? or in TIME_SPEND TextInput)
        ok -6.1 summarize all time spend on all subtasks
        
        -5. instead of sqlite use simple ; separated ascii-text files maybe in this way:
        tasks (root dir)
          \->year 2011
                    \-> month 07
                               \-> task-id: 1.txt
        ok -6.2 continue counting @ the last time spend time
    
        ok -4. button: add status
        user can define his own status
        or select from predefined values: as
        "completed" -> sets status of a task to complete -> background of task will be darkened. a checkbox in preferences allows hiding/showing of completed tasks and also setting the background color individually?
        "billed" -> task was put on a bill and send to the customer
        o when a status is put to a top-level task, it automatically needs to be put to all sub-level tasks and times.
        
        -3. auto update
        -3.1. compile with new air sdk 
        
        -2. it should be okay to have tasks with the same name, because they will be identified by their id, not by the name.
        
        -1. preferences:
        -1.1 show only sub-tasks that belong to the selected task. (otherwise people might get confused)
        
        0. priorities:
        0.1. allow to order the tasks with drag & drop to represent the priority.
        
        1. show tooltip when mouse hovers over a time-entry, how much time was spend in this session
        1.1. show tooltip when mouse hovers over a task-entry, how much time was spend on this task (in total)
        
    ok    2. video how to use this program
        http://www.youtube.com/watch?feature=player_profilepage&v=Mpuo1rlWj5k
        
        3. reporting
        3.1. print a pdf report/overview what you did/was done this month/last month in this and that main-task (root level-task)
        so that you can show it to your boss/customer and make a appropriate bill.
        3.2. report miss-ratio how much a user is "off" with his estimations -> predict how off you will be in the future for certain types of tasks
        
        4. possibility to select multiple tasks and work on multiple tasks at the same time
    
    REQUIREMENTS:
        
        adobe air runtime need to be installed. (http://get.adobe.com/de/air/)
        
        officially supported: windows, osx, linux
        
        it is said, that air should also run on android/iphone.
        
        may the source be with you!

    LICENSE:
    
        this program is free open transparent software.
        
        you can view the source code by simply right-clicking on it.
        
        you can see (when you are knowledgable how to read source code) that i do nothing harmful with your privacy or your data.
        
        i do not send your data to any distant location over encrypted channels, i do not spy on your activities and how long it takes them for you.
        
        i want just to make the world a better place. and: don't be evil.
    
        http://www.gnu.de/documents/gpl-3.0.de.html
    
        
    HOW TO BUILD/COMPILE:
        if you do not have svn, go and install it
    
        video on svn in flash builder 4: http://www.youtube.com/watch?v=9Zy8Yo7na1w
    
        to compile this project you will need the code from:
        
        # Non-members may check out a read-only working copy anonymously over HTTP.
        svn checkout http://tasktracer2.googlecode.com/svn/trunk/ tasktracer2-read-only
    
        (create it as a flex library project, compile it)

        than create a air sdk 2.0 flex-project with this sources:
        
        https://tasktracer2.googlecode.com/svn
        
        and add the airsdk.swc resulting from the library project to the second project.    
    -->
    
    <mx:VBox id="main_box" width="100%" height="100%" resize="{this.width = main_box.width;this.height = main_box.height;}">
        <mx:HBox backgroundColor="white" width="100%" horizontalAlign="center">
            <mx:Label color="black" id="ver" />
            <mx:HBox alpha="0.3">
                <mx:Button label="usage" click="{navigateToURL(new URLRequest(youtubeVideo));}"/>
                <mx:Button label="report issue" click="{navigateToURL(new URLRequest('http://code.google.com/p/tasktracer2/issues/list'));}"/>
            </mx:HBox>
        </mx:HBox>
        <mx:HBox width="100%" horizontalGap="0">
            <mx:HBox id="inputs" width="100%" horizontalGap="0" horizontalScrollPolicy="off">
            </mx:HBox>
            <mx:HBox horizontalGap="2">
                <mx:Button label="ADD COLUMN" click="AddDataGrid();" visible="true" includeInLayout="true" width="98" toolTip="Adds another sub-task-level."/>
                <mx:Button label="UPDATE" click="UPDATE();" visible="true" includeInLayout="true" width="66" toolTip="Updates a selected entry."/>
                <mx:Button label="DELETE" click="DELETE();" visible="true" includeInLayout="true" width="65" toolTip="Deletes a selected entry."/>
            </mx:HBox>
        </mx:HBox>
        <mx:HBox width="100%" height="100%">
            <mx:HBox id="taskGrids" width="100%" height="100%" horizontalGap="0" horizontalScrollPolicy="on" scroll="SyncScroll(event);">
            </mx:HBox>
                <local:DataGrid2 id="timeGrid" height="100%" width="115" click="datagrid_clickTime(event);" dataProvider="{new Array()}"/>
            <mx:VBox horizontalGap="0" verticalGap="0">
                <mx:Label text="CURRENT TIME"/>
                <mx:Label id="TIME_CURRENT" text=""/>
                <mx:HBox horizontalGap="0">
                    <mx:Button label="START" click="StartTimer();" toolTip="Start couting how much time you spend on the selected task."/>
                    <mx:Button label="STOP" click="StopTimer();" toolTip="Stop couting how much time you spend on the selected task."/>
                </mx:HBox>
                <mx:HBox horizontalGap="0">
                    <s:ComboBox id="STATUS" dataProvider="{new ArrayCollection(['complete','billed'])}" width="100%" toolTip="hit enter to defined a custom status" keyDown="KEYDOWN_status(event);"/>
                    <mx:Button label="ADD" width="43" toolTip="Add or update a predifined/custom status to a task and all it's subtasks/times, e.g. mark it as completed etc." click="AddStatus();"/>
                </mx:HBox>
                <mx:Label text="START"/>
                <mx:TextInput id="TIME_START" width="100%"/>
                <mx:Label text="STOP"/>
                <mx:TextInput id="TIME_STOP" width="100%"/>
                
<!-- not in use right now
                
                <mx:Label text="ESTIMATED"/>
                <mx:VBox horizontalGap="0">
                    <mx:HBox horizontalGap="0">
                        <mx:NumericStepper id="DAYS" width="40" minimum="0" maximum="9999" change="CalcEstimate();"/>
                        <mx:Label text="days"/>
                    </mx:HBox>
                    <mx:HBox horizontalGap="0">
                        <mx:NumericStepper id="HOURS" width="40" minimum="0" maximum="9999" change="CalcEstimate();"/>
                        <mx:Label text="hours"/>
                        <mx:NumericStepper id="MINUTES" width="40" minimum="0" maximum="9999" change="CalcEstimate();"/>
                        <mx:Label text="min"/>
                    </mx:HBox>
                </mx:VBox>
-->
                <mx:Label text="TIME FINISHED"/>
                <mx:TextInput id="TIME_ESTIMATED" width="100%"/>
                <mx:Label text="SPEND"/>
                <mx:TextInput id="TIME_SPEND" width="100%"/>
                <mx:Label text="LEFT"/>
                <mx:TextInput id="TIME_LEFT" width="100%"/>
<!--                <mx:Label text="DAILY"/>
                <mx:TextInput id="TIME_DAILY" width="100%"/>
-->            </mx:VBox>
        </mx:HBox>
        <mx:HBox width="100%" horizontalGap="0">
            <mx:TextArea id="COMMENT" width="100%" keyDown="KEYDOWN_comment(event);" toolTip="Hit Ctrl+Enter to add or update a comment." height="85"/>
            <mx:VBox>
                <mx:Button label="ADD COMENT" click="AddUpdateComment(null,COMMENT.text);"/>
                <mx:Label id="LABEL_STATUS" width="150"/>
            </mx:VBox>
        </mx:HBox>
    </mx:VBox>
    
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import flashx.textLayout.elements.SpecialCharacterElement;
            import flashx.textLayout.tlf_internal;
            
            import misc.c_ARRAY;
            import misc.c_DATE;
            import misc.c_OBJECT;
            import misc.c_STRING;
            
            import mx.charts.series.ColumnSeries;
            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.controls.DataGrid;
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.core.Container;
            import mx.effects.Sequence;
            import mx.events.EffectEvent;
            import mx.events.ScrollEvent;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            
            import org.osmf.events.TimeEvent;
            
            import spark.effects.Fade;
            
            import sqllite.air_sqllite_interaction;
            
            // the maximum amount of sub-sub-sub... level of tasks that is allowed.
            public var MaxTasks:int = 10;
            
            public var youtubeVideo:String = "http://www.youtube.com/watch?feature=player_embedded&v=Mpuo1rlWj5k";
            
            public var air_sqllite_interaction_inst:air_sqllite_interaction = null;
            
            
            public var DataBaseLayout:Object = null;
            
            public var c_ARRAY_inst:c_ARRAY = new c_ARRAY();
            public var c_OBJECT_inst:c_OBJECT = new c_OBJECT();
            public var c_STRING_inst:c_STRING = new c_STRING();
            
            public var c_DATE_inst:c_DATE = new c_DATE();
            
            public function initApp():void
            {
                this.addEventListener(Event.CLOSING, onExit);
                var DATE:Date = new Date();
                
                // after creationComplete debug is != null and can be used. Otherwise it would be perfectly okay to create the object during variable declaration.
                air_sqllite_interaction_inst = new air_sqllite_interaction("TaskTracer.sqlite"                               ,null                 ,LOAD_STUFF                  ,HandleFault                 ,null);
                //                                 air_sqllite_interaction(path_and_filename_of_sqllite_database_file:String ,DataBaseLayout:Object,HandleResult:Function = null,HandleFault:Function = null,debug:Object = null)
                // air_sqllite_interaction_inst.addEventListener(SQLEvent.OPEN, LOAD_STUFF);
                
                checkForUpdate();
                
                AddTimeGrid();
            }
            
            import flash.events.ErrorEvent;
            import air.update.ApplicationUpdaterUI;
            import air.update.events.UpdateEvent;
            import mx.controls.Alert;
            
            private var appUpdater:ApplicationUpdaterUI = new ApplicationUpdaterUI();
            
            private function checkForUpdate():void {
                setApplicationVersion(); // Find the current version so we can show it below
                appUpdater.updateURL = "http://praxis2.dyndns.org/tools/TaskTracer/update.xml"; // Server-side XML file describing update
                appUpdater.isCheckForUpdateVisible = false; // We won't ask permission to check for an update
                appUpdater.addEventListener(UpdateEvent.INITIALIZED, onUpdate); // Once initialized, run onUpdate
                appUpdater.addEventListener(ErrorEvent.ERROR, onError); // If something goes wrong, run onError
                appUpdater.initialize(); // Initialize the update framework
            }
            
            private function onError(event:ErrorEvent):void {
                Alert.show(event.toString());
            }
            
            private function onUpdate(event:UpdateEvent):void {
                appUpdater.checkNow(); // Go check for an update now
            }
            
            // Find the current version for our Label below
            private function setApplicationVersion():void {
                var appXML:XML = NativeApplication.nativeApplication.applicationDescriptor;
                var ns:Namespace = appXML.namespace();
                ver.text = "Version " + appXML.ns::version;
            }            
            
            public var LEVEL:int = 0;
            public function LOAD_STUFF(event:Event = null):void
            {
                if(LEVEL == 0)
                {
                    CreateDatabaseLayout();
                }
                else if(LEVEL == 1)
                {
                    DO_LOAD_STUFF = true;
                    readTable(null,"TASKS");
                }
                else if(LEVEL == 2)
                {
                    readTable(null,"TIMES");
                }
                else if(LEVEL == 3)
                {
                    DO_LOAD_STUFF = false;
                    ShowTime();
                }
                LEVEL++;
            }
            
            /** will hold all the tables from the database **/
            public var TABLES:Object = {TASKS:"",TIMES:"",COMMENTS:"",DATAPROVIDERS:new Object()};
            /** NAME OF THE TABLE that is currently fetched from database **/
            public var CURRENT_TABLE_NAME:String = "";
            
            public function readTable2(event:Event = null):void
            {
                readTable(null,CURRENT_TABLE_NAME);
            }
            
            public function readTableTasksAndTimes(event:Event = null):void
            {
                CURRENT_TABLE_NAME = "TASKS";
                readTable(null,CURRENT_TABLE_NAME);
                
                CURRENT_TABLE_NAME = "TIMES";
                readTable(null,CURRENT_TABLE_NAME);
            }
            
            var DO_LOAD_STUFF:Boolean = false;
            public function readTable(event:Event = null,TABLENAME:String = ""):void
            {
                if(event == null)
                {
                    if(c_OBJECT_inst.not_empty(TABLENAME))
                    {
                        CURRENT_TABLE_NAME = TABLENAME;
                        air_sqllite_interaction_inst.READTABLE(null,TABLENAME,readTable);
                    }
                }
                else
                {
                    if(event is ResultEvent)
                    {
                        var RESULTEVENT:ResultEvent = event as ResultEvent;
                        
                        TABLES["DATAPROVIDERS"] = new Array();
                        
                        TABLES[CURRENT_TABLE_NAME] = RESULTEVENT.result;
                        
                        var MAX_PARENT_COUNT:int = 0;
                        
                        if(CURRENT_TABLE_NAME == "TASKS")
                        {
                            var ARRAY:Array = RESULTEVENT.result as Array;
                            
                            for each(var TASK:Object in ARRAY)
                            {
                                if(TASK["PARENTTASKID"] == "")
                                {
                                    if(TABLES["DATAPROVIDERS"][0] == null)
                                    {
                                        TABLES["DATAPROVIDERS"][0] = new Array();
                                    }
                                    TABLES["DATAPROVIDERS"][0].push(TASK);
                                }
                                else
                                {
                                    recursionCount = 0;
                                    var PARENT_COUNT:int = ParentCount(ARRAY,TASK);
                                    
                                    if(PARENT_COUNT > MAX_PARENT_COUNT)
                                    {
                                        MAX_PARENT_COUNT = PARENT_COUNT;
                                    }
                                    
                                    if(TABLES["DATAPROVIDERS"][PARENT_COUNT] == null)
                                    {
                                        TABLES["DATAPROVIDERS"][PARENT_COUNT] = new Array();
                                    }
                                    
                                    TABLES["DATAPROVIDERS"][PARENT_COUNT].push(TASK);
                                }
                            }
                            
                            // check if there are more datagrids than data
                            while(taskGrids.getChildren().length > (MAX_PARENT_COUNT+1))
                            {
                                var pos:int = taskGrids.getChildren().length-1;
                                taskGrids.removeChildAt(pos);
                                inputs.removeChildAt(pos);
                            }
                            
                            // die TABLES["DATAPROVIDERS"] den dataGrids zuweisen
                            for(var counter:int = 0;!(counter > MAX_PARENT_COUNT);counter++)
                            {
                                var DATAGRID2:DataGrid2 = null;
                                
                                // are we in init phase? or just doing a dataprovider refresh
                                if(DO_LOAD_STUFF || (taskGrids.getChildren().length == 0))
                                {
                                    DATAGRID2 = AddDataGrid(null,"TASK");
                                }
                                else
                                {
                                    DATAGRID2 = taskGrids.getChildAt(counter) as DataGrid2;
                                }
                                
                                DATAGRID2.dataGrid.dataProvider = TABLES["DATAPROVIDERS"][counter];
                                DATAGRID2.dataProvider = TABLES["DATAPROVIDERS"][counter];
                                // selectedDatagrid.dataGrid.dataProvider = TABLES["DATAPROVIDERS"][counter];
                            }
                        }
                        else if(CURRENT_TABLE_NAME == "TIMES")
                        {
                            var RESULTEVENT:ResultEvent = event as ResultEvent;
                            
                            TABLES[CURRENT_TABLE_NAME] = new Array();
                            TABLES[CURRENT_TABLE_NAME] = RESULTEVENT.result;
                            
                            datagrid_click(new MouseEvent(MouseEvent.CLICK,false,false,null,null,timeGrid));
                        }
                        
                        if(DO_LOAD_STUFF)
                        {
                            LOAD_STUFF();
                        }
                        
                        if(itemChanged_while_Timer_running_useLastSelectedItem)
                        {
                            itemChanged_while_Timer_running_useLastSelectedItem = false;
                        }
                        else
                        {
                            ReSelected();
                        }
                    }
                    else if(event is FaultEvent)
                    {
                        var FAULTEVENT:FaultEvent = event as FaultEvent;
                        trace(FAULTEVENT);
                    }
                }
                
            }

            /** o 7.1. after update/add of task or time the last selected task must again be selected again **/
            public function ReSelected():void
            {
                if(c_OBJECT_inst.not_empty(lastSelectedDataGrid))
                {
                    for each(var ELEMENT:Object in lastSelectedDataGrid.dataGrid.dataProvider["source"])
                    {
                        if(ELEMENT["id"] == lastSelectedItem["id"])
                        {
                            lastSelectedDataGrid.dataGrid.selectedItem = ELEMENT;
                            selectedDatagrid = lastSelectedDataGrid;
                        }
                    }
                    
                    // set selectedDatagrid and select task partens
                    datagrid_click(new MouseEvent(MouseEvent.CLICK,false,false,null,null,lastSelectedDataGrid));
                }
            }
            
            public function FindParentTask(ARRAY:Array,PARENTTASKID:String):Object
            {
                var RESULT:Object = null;
                for each(var ELEMENT:Object in ARRAY)
                {
                    if(ELEMENT["id"] == PARENTTASKID)
                    {
                        RESULT = ELEMENT;
                        break;
                    }
                }
                
                return RESULT;
            }
            
            var recursionCount:int = 0;
            public function ParentCount(TASKS:Array,TASK:Object):int
            {
                if(TASK["PARENTTASKID"] != "")
                {
                    var TASK:Object = FindParentTask(TASKS,TASK["PARENTTASKID"])
                    
                    if(TASK == null)
                    {
                        return recursionCount;
                    }
                    recursionCount++;
                    
                    if(TASK["PARENTTASKID"] == "")
                    {
                        return recursionCount;
                    }
                    else
                    {
                        ParentCount(TASKS,TASK);
                    }
                }
                
                return recursionCount;
            }
            
            public function FindParentTaskID(ARRAY:Array,TASK:Object):String
            {
                var RESULT:String = "";
                var TASK:Object = FindParentTask(ARRAY,TASK["id"]);
                RESULT = TASK["id"];
                return RESULT;
            }
            
            /** the currently selected datagrid **/
            public var selectedDatagrid:DataGrid2 = null;
            public var lastSelectedDataGrid:DataGrid2 = null;
            public var lastSelectedItem:Object = null;
            public var parentDatagrid:DataGrid2 = null;
            public var selectedInput:Object = null;
            
            public function HandleFault(event:Event):void
            {
                if(event is SQLErrorEvent)
                {
                    var ERROR:String = event['error']['message']+" "+event['error']['details'];
                    Alert.show(ERROR,"SQLITE ERROR");
                }
            }
            
            public function UPDATE(ELEMENT:Object = null):void
            {
                if(selectedDatagrid.dataGrid.selectedItem != null)
                {
                    var UPDATE_ELEMENT:Object = c_OBJECT_inst.copy(selectedDatagrid.dataGrid.selectedItem);
                    delete UPDATE_ELEMENT["mx_internal_uid"];
                    if(UPDATE_ELEMENT.hasOwnProperty("hide"))
                    {
                        delete UPDATE_ELEMENT["hide"];
                    }
                    UPDATE_ELEMENT["STATUS"] = STATUS.textInput.text; 
                    
                    for(var counter:int = 0;!(counter == taskGrids.getChildren().length);counter++)
                    {
                        if(taskGrids.getChildAt(counter) == selectedDatagrid)
                        {
                            selectedInput = inputs.getChildAt(counter) as TextInput;
                            
                            if(selectedDatagrid.parent == taskGrids)
                            {
                                CURRENT_TABLE_NAME = "TASKS";
                                UPDATE_ELEMENT["TASK"] = selectedInput.text; 
                                air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,UPDATE_ELEMENT,UPDATE_ELEMENT.id,readTable2)
                            }
                            else if(selectedDatagrid.parent == timeGrid)
                            {
                                CURRENT_TABLE_NAME = "TIMES";
                                UPDATE_ELEMENT["TIME"] = selectedInput.text; 
                                air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,UPDATE_ELEMENT,UPDATE_ELEMENT.id,readTable2)
                            }
                            break;
                        }
                    }
                }
                else
                {
                    Alert.show("Can not update element","No element selected");
                }
            }
            
            public function UPDATE_ELEMENT(ELEMENT:Object = null,TABLE:String = "TASKS"):void
            {
                delete ELEMENT["mx_internal_uid"];
                if(ELEMENT.hasOwnProperty("hide"))
                {
                    delete ELEMENT["hide"];
                }
                
                CURRENT_TABLE_NAME = TABLE;
                air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,ELEMENT,ELEMENT.id,readTable2,readTable2)
            }

            /**
             TIMES = Array (@1a135039)    
                 COMMENT = null -> user can comment on this record    
                 id = 1    
                 STATUS = "" -> billed, completed... custom status possible    
                 TASKID = "4"    -> with what task is this time record associated
                 TIME = "2011:07:12 17:59:12"    
                 TIME_DIFF = "3648" -> difference in MSeconds between start and stop
                 TIME_ESTIMATED = "1310486352194" -> seconds since estimated - start
                 TIME_LEFT = ""    
                 TIME_START = "1310486352194" -> when the task was started (milliseconds since 1970)
                 TIME_STOP = "1310486355842"    -> when the task was stoped (milliseconds since 1970)

                 TASKS = Array (@1af5c809)    
                COMMENT = null    -> user can comment on this record
                PARENTTASKID = "" -> if this task is a child of another parent-task, save parentID here
                SPEND = null -> time in seconds that was spend on this task and all subtasks
                STATUS = "" -> billed, completed... custom status possible
                TASK = "task1" -> the name of the task
                TIMECREATED = "1310471342508" -> when the task was created (milliseconds since 1970)
            **/
            public function insertRecord(TABLENAME:String,ELEMENT:Object,onComplete:Function):void
            {
                CURRENT_TABLE_NAME = TABLENAME;
                
                if(ELEMENT.hasOwnProperty("hide"))
                {
                    delete ELEMENT["hide"];
                }
                
                if(ELEMENT.hasOwnProperty("mx_internal_uid"))
                {
                    delete ELEMENT["mx_internal_uid"];
                }
                
                air_sqllite_interaction_inst.INSERT(null,CURRENT_TABLE_NAME,ELEMENT,onComplete,HandleFault);
            }

            public var currentTime:Object = new Object();
            /**
             *     ok -6. how much time spend on each task (tooltip? or in TIME_SPEND TextInput)
                ok -6.1 summarize all time spend on all subtasks
                
                -5. instead of sqlite use simple ; separated ascii-text files maybe in this way:
                tasks (root dir)
                  \->year 2011
                            \-> month 07
                                       \-> task-id: 1.txt
                ok -6.2 continue counting @ the last time spend time
                                       * 
             * adds time record to task 
             * currentTime = {TASKID:selectedDatagrid.dataGrid.selectedItem["id"],
             TIME_ESTIMATED:DATE_ESTIMATED.getTime(),
             TIME_START:DATE_START.getTime(),
             TIME_STOP:DATE_STOP.getTime(),
             TIME_DIFF:DATE_DIFF_STRING,
             TIME_LEFT:""};
             **/
            public function AddTime(event:Event = null,time:Object = null):void
            {
                if(event == null)
                {
                    if(selectedDatagrid.dataGrid.selectedItem != null)
                    {
                        var DATE:Date = new Date();
                        var DATE_STRING:String = DATE.getTime().toString();
                        CURRENT_TABLE_NAME = "TIMES";

                        if(itemChanged_while_Timer_running_useLastSelectedItem)
                        {
                            time["TASKID"] = lastSelectedItem["id"];
                        }
                        else
                        {
                            time["TASKID"] = selectedDatagrid.dataGrid.selectedItem["id"];
                        }
                        
                        currentTime = time;
                        
                        insertRecord(CURRENT_TABLE_NAME,currentTime,AddTime);
                        
                        if(isNaN(selectedDatagrid.dataGrid.selectedItem["SPEND"]))
                        {
                            selectedDatagrid.dataGrid.selectedItem["SPEND"] = 0;
                        }
                    }
                    else
                    {
                        NoTask("can not add time");
                    }
                }
                else
                {
                    var ITEMS_TO_UPDATE:Array = new Array();
                    
                    var currentlySelectedItem:Object = null; 

                    if(itemChanged_while_Timer_running_useLastSelectedItem)
                    {
                        currentlySelectedItem = lastSelectedItem;
                    }
                    else
                    {
                        currentlySelectedItem = selectedDatagrid.dataGrid.selectedItem;
                    }
                    
                    var SPEND_UNTIL_NOW:Number = parseFloat(currentlySelectedItem["SPEND"]);
                    TIME_SPEND_CURRENT_SESSION_AND_ALL_SUBTASKS_IN_SECONDS = TIME_SPEND_CURRENT_SESSION_IN_SECONDS; // SPEND_UNTIL_NOW + TIME_SPEND_CURRENT_SESSION_IN_SECONDS
                    
                    TIME_SPEND.text = c_DATE_inst.SecondsToYearMonthDayHoursMinutesSeconds( TIME_SPEND_CURRENT_SESSION_AND_ALL_SUBTASKS_IN_SECONDS );

                    currentlySelectedItem["SPEND"] = TIME_SPEND_CURRENT_SESSION_AND_ALL_SUBTASKS_IN_SECONDS;
                    ITEMS_TO_UPDATE.push(currentlySelectedItem);
                    
                    var currentChildTask:Object = currentlySelectedItem;
                    
                    var PARENTTASK:Object = FindParentTask(TABLES["TASKS"],currentChildTask["PARENTTASKID"]);
                    // update the times of all parent tasks
                    do
                    {
                        if(isNaN(PARENTTASK["SPEND"]))
                        {
                            PARENTTASK["SPEND"] = 0;
                        }
                        /** subtract previous value and add new value **/
                        if(parseFloat(PARENTTASK["SPEND"]) != 0)
                        {
                            PARENTTASK["SPEND"] = parseFloat(PARENTTASK["SPEND"]) - TIME_SPEND_BEFORE_CURRENT_SESSION;
                        }

                        PARENTTASK["SPEND"] = parseFloat(PARENTTASK["SPEND"]) + TIME_SPEND_CURRENT_SESSION_IN_SECONDS;
                        
                        ITEMS_TO_UPDATE.push(PARENTTASK);
                        
                        currentChildTask = PARENTTASK;
                        
                        PARENTTASK = FindParentTask(TABLES["TASKS"],currentChildTask["PARENTTASKID"]);
                    }
                    while(c_OBJECT_inst.not_empty(PARENTTASK))

                    UPDATE_MULTIPLE_ITEMS(null,ITEMS_TO_UPDATE,"TASKS");

                    TIME_SPEND_CURRENT_SESSION_IN_SECONDS = 0;

                    // readTable(null,"TIMES");
                }
            }
            
            public function NoTask(error:String):void
            {
                Alert.show("no task was selected",error);                
            }
            
            public var itemChanged_while_Timer_running_useLastSelectedItem:Boolean = false;

            public function datagrid_clickTime(event:MouseEvent):void
            {
                trace(timeGrid.dataGrid.selectedItem);
            }

            /** 
             * 7. automatically select parent-tasks when selecting a subtask... and deselect all other tasks right of this task.
             * and display times of this task
             * 
             * -1.1 show only sub-tasks that belong to the selected task. (otherwise people might get confused)
             * 
             * **/
            public function datagrid_click(event:MouseEvent):void
            {
                timeGrid.dataGrid.selectedItem = null;
                if(event.currentTarget is DataGrid2)
                {
                    // we have a new selectedDatagrid
                    selectedDatagrid = event.currentTarget as DataGrid2;

                    // * -7.1. switching tasks resets time counter -> not good, counter stop, add save time before resetting
                    // counter stop, add save time before resetting
                    if(lastSelectedItem != selectedDatagrid.dataGrid.selectedItem)
                    {
                        if(TIMER != null)
                        {
                            if(TIMER.running)
                            {
                                itemChanged_while_Timer_running_useLastSelectedItem = true;
                                onExit();
                            }
                        }
                        else
                        {
                            // REMEMBER what was selected last time
                            lastSelectedItem = c_OBJECT_inst.copy(selectedDatagrid.dataGrid.selectedItem);
                            lastSelectedDataGrid = selectedDatagrid;
                        }
                    }
                    
                }
                if(selectedDatagrid != null)
                {
                    if(selectedDatagrid.dataGrid.selectedItem != null)
                    {
                        for(var counter:int = 0;!(counter == taskGrids.getChildren().length);counter++)
                        {
                            if(taskGrids.getChildAt(counter) == selectedDatagrid)
                            {
                                selectedInput = inputs.getChildAt(counter) as TextInput;
                                
                                if(selectedDatagrid.dataGrid.selectedItem.hasOwnProperty("TASK"))
                                {
                                    selectedInput.text = selectedDatagrid.dataGrid.selectedItem["TASK"];
                                }
                                else if(selectedDatagrid.dataGrid.selectedItem.hasOwnProperty("TIME"))
                                {
                                    selectedInput.text = selectedDatagrid.dataGrid.selectedItem["TIME"];
                                }
                                
                                selectParentTask(selectedDatagrid);
                                
                                deselectDataGridsRightOf(selectedDatagrid);
                                
                                displayTaskProperties();
                                
                                hideNotRelatedTasks();
                                
                                break;
                            }
                        }
                    }                
                }
                
                if(DO_LOAD_STUFF == false)
                {
                    // and display times of this task
                    if(TABLES["TIMES"] != null)
                    {
                        if(selectedDatagrid.dataGrid.selectedItem != null)
                        {
                            var DATAPROVIDER:Array = new Array();
                            for each(var time:Object in TABLES["TIMES"])
                            {
                                if(time["TASKID"] == selectedDatagrid.dataGrid.selectedItem["id"])
                                {
                                    DATAPROVIDER.push(time);
                                }
                            }
                            timeGrid.dataGrid.dataProvider = DATAPROVIDER;
                        }
                    }
                }
            }
            
/**             -1. preferences:
            -1.1 show only sub-tasks that belong to the selected task. (otherwise people might get confused)
             * exactly the other way around than "select parent task" the task columns need to be processed top-down
             * from left to right.
             * showing only the tasks that belong to the selected parent-task.
             * 
             * but always show all root (level0) tasks.
             * **/
            public function hideNotRelatedTasks():void
            {
                var pos:int = positionOfIn(selectedDatagrid,taskGrids);
                
                var ChildDG:DataGrid2 = null;
                try
                {
                    ChildDG = taskGrids.getChildAt(pos+1) as DataGrid2;
                }
                catch(e:Error)
                {
                    ChildDG = null;
                }
                
                if(ChildDG != null)
                {
                    if((ChildDG.dataProvider != null) && (selectedDatagrid.dataGrid.selectedItem != null))
                    {
                        if(ChildDG.dataProvider != null)
                        {
                            if(ChildDG.dataProvider["length"] != 0)
                            {
                                ChildDG.dataProvider = ChildDG.dataProvider;

                                for each(var TASK:Object in ChildDG.dataProvider)
                                {
                                    if(TASK["PARENTTASKID"] == selectedDatagrid.dataGrid.selectedItem["id"])
                                    {
                                        TASK["hide"] = false;
                                    }
                                    else
                                    {
                                        TASK["hide"] = true;
                                    }
                                }
                                ChildDG.filteredDataProvider = ChildDG.dataProvider;
                            }
                        }
                    }
                    
                }
                
                // empty the rest of the datagrids right from the current parent/child datagrid
                while((pos+2) < taskGrids.getChildren().length)
                {
                    ChildDG = taskGrids.getChildAt(pos+2) as DataGrid2;
                    for each(var TASK:Object in ChildDG.dataProvider)
                    {
                        TASK["hide"] = true;
                    }
                    ChildDG.filteredDataProvider = ChildDG.dataProvider;
                    pos++;
                }
            }
            
            public var AddingComment:Boolean = false;
            
            /** adds/updates a comment of a task/time
             depending on what is selected a comment belonging to a task
             * or a comment belonging to a time must be updated
             * 
             *     -7. Adding a comment resets time counter!?
             * **/
            public function AddUpdateComment(event:Event = null,COMMENT_STRING:String = ""):void
            {
                if(event == null)
                {
                    if(selectedDatagrid.dataGrid.selectedItem != null)
                    {
                        var UPDATE_ELEMENT:Object = null;
                        // is it a time?
                        if(timeGrid.dataGrid.selectedItem != null)
                        {
                            CURRENT_TABLE_NAME = "TIMES";
                            UPDATE_ELEMENT = c_OBJECT_inst.copy(timeGrid.dataGrid.selectedItem);
                            
                            // UPDATE_ELEMENT["TIME"] = selectedInput.text; 
                            // selectedDatagrid.dataGrid.selectedItem["TIME_DIFF"] = ""; // TIME_START - TIME_STOP;
                        }
                        else
                        {
                            UPDATE_ELEMENT = c_OBJECT_inst.copy(selectedDatagrid.dataGrid.selectedItem);
                            // it is a task
                            CURRENT_TABLE_NAME = "TASKS";
                        }
                        
                        if(c_OBJECT_inst.not_empty(UPDATE_ELEMENT))
                        {
                            // delete this
                            delete UPDATE_ELEMENT["mx_internal_uid"];
                            if(UPDATE_ELEMENT.hasOwnProperty("hide"))
                            {
                                delete UPDATE_ELEMENT["hide"];
                            }
                            // add that
                            AddingComment = true;
                            UPDATE_ELEMENT["COMMENT"] = COMMENT.text;
    
                            // update
                            air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,UPDATE_ELEMENT,UPDATE_ELEMENT.id,AddUpdateComment)
                        }
                    }
                    else
                    {
                        Alert.show("sorry master, i do not know with what task i should associate this comment.","Please select a task");
                    }
                }
                else if(event.type == SQLEvent.RESULT)
                {
                    DisplayStatus(null,"Comment added/updated.",LABEL_STATUS);
                    // DisplayStatus(event:Event = null,STATUS:String = "",DISPLAYOBJECT:DisplayObject = null,SECONDS:int = 3):void

                    readTable2();
                }
            }
            
            /** display comment of a task, if it exists -> selectedDatagrid.dataGrid.selectedItem **/
            public function displayTaskProperties():void
            {
                if(selectedDatagrid.dataGrid.selectedItem != null)
                {
                    // COMMENT
                    COMMENT.text = "";
                    if(c_OBJECT_inst.empty(selectedDatagrid.dataGrid.selectedItem["COMMENT"]))
                    {
                        selectedDatagrid.dataGrid.selectedItem["COMMENT"] = ""; // display "" instead of null
                    }
                    else
                    {
                        COMMENT.text = selectedDatagrid.dataGrid.selectedItem["COMMENT"]
                    }
                        
                    // STATUS
                    STATUS.selectedItem = "";
                    if(c_OBJECT_inst.empty(selectedDatagrid.dataGrid.selectedItem["STATUS"]))
                    {
                        selectedDatagrid.dataGrid.selectedItem["STATUS"] = ""; // display "" instead of null
                    }
                    else
                    {
                        STATUS.selectedItem = selectedDatagrid.dataGrid.selectedItem["STATUS"]
                    }
                    
                    // TIME SPEND
                    if(c_OBJECT_inst.empty(selectedDatagrid.dataGrid.selectedItem["SPEND"]))
                    {
                        selectedDatagrid.dataGrid.selectedItem["SPEND"] = 0; // display "" instead of null
                        TIME_SPEND.text = "0 seconds";
                        TIME_SPEND_CURRENT_SESSION_IN_SECONDS = 0;
                    }
                    else
                    {
                        TIME_SPEND.text = c_DATE_inst.SecondsToYearMonthDayHoursMinutesSeconds( parseFloat(selectedDatagrid.dataGrid.selectedItem["SPEND"]) );

                        // -7. Adding a comment resets time counter!?
                    }
                }
                else
                {
                    Alert.show("sorry my master may i ask you humbly... ","... to select a task or time to display it's comment.");
                }
            }
            
            var TIMER:Timer = null;
            var DATE_CURRENT:Date = null; 
            var DATE_START:Date = null; 
            var DATE_STOP:Date = null; 
            var DATE_SPEND:Date = null;
            /** the time that is spend on the current session/task/time between start and stop **/
            var TIME_SPEND_CURRENT_SESSION_IN_SECONDS:Number = 0;
            /** the time that is spend on the current session/task/time between start and stop and all subtasks **/
            var TIME_SPEND_CURRENT_SESSION_AND_ALL_SUBTASKS_IN_SECONDS:Number = 0;
            /** the value that was spend on the time before the current session **/
            var TIME_SPEND_BEFORE_CURRENT_SESSION:Number = 0;
            var DATE_ESTIMATED:Date = null; 
            var DATE_LEFT:Date = null; 
            
            /** start time taking/logging **/
            public function StartTimer(event:TimerEvent = null):void
            {
                if(event == null)
                {
                    if(TIMER != null)
                    {
                        if(TIMER.running)
                        {
                            StopTimer();
                        }
                    }
                    DATE_START = new Date();
                    TIMER = new Timer(1000,Infinity);
                    TIMER.addEventListener(TimerEvent.TIMER, StartTimer);
                    TIMER.addEventListener(TimerEvent.TIMER_COMPLETE, StartTimer);
                    TIME_START.text = c_DATE_inst.date_to_string(DATE_START,"YYYY:MM:DD",true);
                    TIMER.start();
                    
                    if(isNaN(selectedDatagrid.dataGrid.selectedItem["SPEND"]))
                    {
                        selectedDatagrid.dataGrid.selectedItem["SPEND"] = 0;
                    }
                    TIME_SPEND_CURRENT_SESSION_IN_SECONDS = parseFloat(selectedDatagrid.dataGrid.selectedItem["SPEND"]);
                    TIME_SPEND_BEFORE_CURRENT_SESSION = parseFloat(selectedDatagrid.dataGrid.selectedItem["SPEND"]);
                }
                else if(event.type == TimerEvent.TIMER)
                {
                    TIME_SPEND.text = c_DATE_inst.SecondsToYearMonthDayHoursMinutesSeconds( TIME_SPEND_CURRENT_SESSION_IN_SECONDS );

                    CalcEstimate();
                    
                    TIME_SPEND_CURRENT_SESSION_IN_SECONDS++;
                }
                else if(event.type == TimerEvent.TIMER_COMPLETE)
                {
                }
            }
            
            /** display current time **/
            public function ShowTime(event:TimerEvent = null):void
            {
                if(event == null)
                {
                    var SHOW_TIMER:Timer = new Timer(1000,Infinity);
                    SHOW_TIMER.addEventListener(TimerEvent.TIMER, ShowTime);
                    SHOW_TIMER.addEventListener(TimerEvent.TIMER_COMPLETE, ShowTime);
                    SHOW_TIMER.start();
                }
                else if(event.type == TimerEvent.TIMER)
                {
                    DATE_CURRENT = new Date();
                    TIME_CURRENT.text = c_DATE_inst.date_to_string(DATE_CURRENT,"YYYY.MM.DD",true);
                }
                else if(event.type == TimerEvent.TIMER_COMPLETE)
                {
                }
            }
            
            /** stop time taking/logging/add a time record **/
            public function StopTimer(selectedItem:Object = null):void
            {
                if(TIMER.running)
                {
                    if(selectedDatagrid.dataGrid.selectedItem != null)
                    {
                        DATE_STOP = new Date();
                        currentTime["TIME_STOP"] = DATE_STOP.getTime(); 
                        TIME_STOP.text = c_DATE_inst.date_to_string(DATE_STOP,"YYYY:MM:DD",true);
                        TIMER.stop();
                        // TIMER = null;
                        
                        var DATE_DIFF_STRING:String = (DATE_STOP.getTime() - DATE_START.getTime()).toString();
                        
                        var time:Object = {
                            TIME_ESTIMATED:DATE_ESTIMATED.getTime(),
                                TIME_START:DATE_START.getTime(),
                                TIME:TIME_START.text,
                                TIME_STOP:DATE_STOP.getTime(),
                                TIME_DIFF:DATE_DIFF_STRING,
                                TIME_LEFT:""};
                        
                        AddTime(null,time);
                    }
                    else
                    {
                        NoTask("can not add time");
                    }
                }
            }
            
            /** **/
            public function AddTask(event:Event = null):void
            {
                if(event == null)
                {
                    var DATE:Date = new Date();
                    var DATE_STRING:String = DATE.getTime().toString();
                    
                    var PARENTTASKID:String = "";
                    var STATUS:String = "";
                    
                    var proceed:Boolean = true;
                    
                    var pos:int = positionOfIn(selectedInput,inputs);
                    
                    if(pos > 0)
                    {
                        if(c_OBJECT_inst.not_empty(selectedDatagrid.dataGrid.selectedItem))
                        {
                            proceed = true;
                        }
                        else
                        {
                            Alert.show("This sub task needs to be associated with a parent task, please select one.","No Parent-Task-Selected");
                            proceed = false;
                        }
                    }
                    
                    if(proceed)
                    {
                        selectedDatagrid = null;
                        
                        try
                        {
                            selectedDatagrid = taskGrids.getChildAt(pos) as DataGrid2;
                        }
                        catch(e:Error)
                        {
                            // there is no datagrid yet, create one
                            AddDataGrid(null,"TASK");
                            // then try again
                            selectedDatagrid = taskGrids.getChildAt(pos) as DataGrid2;
                        }
                        
                        if(pos > 0)
                        {
                            PARENTTASKID = getParentTaskID(selectedDatagrid);                    
                            if(c_OBJECT_inst.empty(PARENTTASKID))
                            {
                                Alert.show("This sub task needs to be associated with a parent task, please select one.","No Parent-Task-Selected");
                                proceed = false;
                            }
                        }
                        
                        // o 2. check that adding task with the same name as previous task can not be done.
                         for each(var TASK:Object in selectedDatagrid.dataGrid.dataProvider)
                        {
                            lastSelectedDataGrid
                            if(TASK["TASK"] == selectedInput["text"])
                            {
                                Alert.show("A task with this name allready exists.","My master, i am sorry, but it would be wise to enter a different task name.");
                                proceed = false;
                            }
                        }

                        // o do not allow emtpy tasks
                        if(c_OBJECT_inst.empty(selectedInput["text"]))
                        {
                            Alert.show("Please enter a Task","No Task was entered.");
                            proceed = false;
                        }
                        if(proceed)
                        {
                            var ELEMENT:Object = {TASK:selectedInput["text"],PARENTTASKID:PARENTTASKID ,STATUS:STATUS,TIMECREATED:DATE_STRING,SPEND:0};
                            //                    TASK:'TEXT'               ,PARENTTASKID:'TEXT'       ,STATUS:'TEXT',TIMECREATED:'TEXT'     ,SPEND};
                            insertRecord("TASKS",ELEMENT,AddTask);
                        }
                    }
                    
                }
                else if(event.type == SQLEvent.RESULT)
                {
                    readTable(null,"TASKS");
                }
            }
            
            /** tries to figure out, what is the id of the parent of the selected TASK **/
            public function getParentTaskID(DATAGRID2:DataGrid2):String
            {
                var RESULT:String = "";
                var pos:int = positionOfIn(DATAGRID2,taskGrids);
                if(pos > 0)
                {
                    parentDatagrid = DATAGRID2 = taskGrids.getChildAt(pos-1) as DataGrid2;
                    
                    if(parentDatagrid.dataGrid.selectedItem != null)
                    {
                        RESULT = parentDatagrid.dataGrid.selectedItem["id"];
                    }
                }
                
                return RESULT;
            }
            
            /** checks what childs belong to the given task,
             * what tasks have PARENTTASKID of current task and return them **/
            public function findChildTasks(TASK:Object):Array 
            {
                var CHILD_TASKS:Array = new Array(); 
                var TASKS:Array = TABLES["TASKS"];
                
                for(var counter:int = 0;!(counter == TASKS.length);counter++)
                {
                    if(TASK["id"] == TASKS[counter]["PARENTTASKID"])
                    {
                        CHILD_TASKS.push(TASKS[counter]);
                    }
                }
                
                return CHILD_TASKS;
            }
            
            /** select parent task, return parent task **/
            public function selectParentTask(selectedDatagrid:DataGrid2):Object
            {
                var RESULT:Object = null;
                var pos:int = positionOfIn(selectedDatagrid,taskGrids);
                if(pos > 0)
                {
                    parentDatagrid = taskGrids.getChildAt(pos-1) as DataGrid2;
                    
                    for each(var TASK:Object in parentDatagrid.dataGrid.dataProvider)
                    {
                        if(TASK["id"] == selectedDatagrid.dataGrid.selectedItem["PARENTTASKID"])
                        {
                            parentDatagrid.dataGrid.selectedItem = TASK;
                            RESULT = TASK;
                            break;
                        }
                    }
                    
                    selectParentTask(parentDatagrid);
                }
                
                return RESULT;
            }
            
            /** deselect all selected items right of the current selected DataGrid **/
            public function deselectDataGridsRightOf(selectedDatagrid:DataGrid2):void
            {
                var pos:int = positionOfIn(selectedDatagrid,taskGrids);
                
                pos++;
                
                // deselect all datagrids right of this datagrid
                while(pos < taskGrids.getChildren().length)
                {
                    var DATAGRID2:DataGrid2 = taskGrids.getChildAt(pos) as DataGrid2;
                    DATAGRID2.dataGrid.selectedItem = null;
                    pos++;
                }
            }
            
            /** returns the position-index of a DisplayObject in a container, returns
             * -1 if the child was not found in the container **/
            public function positionOfIn(selectedInput:Object,CONTAINER:Container):int
            {
                var counter:int = -1;
                var CHILDREN:int = CONTAINER.getChildren().length;
                for(counter = 0;!(counter == CHILDREN);counter++)
                {
                    var ELEMENT:Object = CONTAINER.getChildAt(counter);
                    if(ELEMENT == selectedInput)
                    {
                        return counter;
                    }
                }
                
                return counter;
            }
            
            /** deletes a task or time **/
            public function DELETE():void
            {
                if(selectedDatagrid.dataGrid.selectedItem != null)
                {
                    if(selectedDatagrid.parent == taskGrids)
                    {
                        CURRENT_TABLE_NAME = "TASKS";
                        air_sqllite_interaction_inst.DELETE(null,CURRENT_TABLE_NAME,selectedDatagrid.dataGrid.selectedItem.id,readTable2,HandleFault);
                    }
                    if(selectedDatagrid.parent == timeGrid)
                    {
                        CURRENT_TABLE_NAME = "TIMES";
                        air_sqllite_interaction_inst.DELETE(null,CURRENT_TABLE_NAME,selectedDatagrid.dataGrid.selectedItem.id,readTable2,HandleFault);
                    }
                }
                else
                {
                    Alert.show("Sorry dude, can not delete element","No element selected");                    
                }
            }

            public var TABLECOUNT:int = 0;
            /** 
             * currently the program only needs 2x tables: TASKS and TIMES
             * currently it has this layout: ( i hope i do not forget to update this comment )
             *  
             * TASKS = Array (@1af5c809)    
                    COMMENT = null    -> user can comment on this record
                    PARENTTASKID = "" -> if this task is a child of another parent-task, save parentID here
                    SPEND = null -> time in seconds that was spend on this task and all subtasks
                    STATUS = "" -> billed, completed... custom status possible
                    TASK = "task1" -> the name of the task
                    TIMECREATED = "1310471342508" -> when the task was created (milliseconds since 1970)

                 TIMES = Array (@1a135039)    
                        COMMENT = null -> user can comment on this record    
                        id = 1    
                        STATUS = "" -> billed, completed... custom status possible    
                        TASKID = "4"    -> with what task is this time record associated
                        TIME = "2011:07:12 17:59:12"    
                        TIME_DIFF = "3648" -> difference in MSeconds between start and stop
                        TIME_ESTIMATED = "1310486352194" -> seconds since estimated - start
                        TIME_LEFT = ""    
                        TIME_START = "1310486352194" -> when the task was started (milliseconds since 1970)
                        TIME_STOP = "1310486355842"    -> when the task was stoped (milliseconds since 1970)
             * **/
            public function CreateDatabaseLayout(event:Event = null):void
            {
                if(TABLECOUNT == 0)
                {
                    // create tables if not exist 
                    DataBaseLayout = {TableName:'TASKS',TASK:'TEXT',PARENTTASKID:'TEXT',STATUS:'TEXT',TIMECREATED:'TEXT',COMMENT:'TEXT',STATUS:'TEXT'};
                    air_sqllite_interaction_inst.CreateTable(null,DataBaseLayout,CreateDatabaseLayout,CreateDatabaseLayout);
                }
                else if(TABLECOUNT == 1)
                {
                    DataBaseLayout = {TableName:'TIMES',TASKID:'TEXT',TIME_ESTIMATED:'TEXT',TIME_START:'TEXT',TIME:'TEXT',TIME_STOP:'TEXT',TIME_DIFF:'TEXT',TIME_LEFT:'TEXT',COMMENT:'TEXT',STATUS:'TEXT'};
                    air_sqllite_interaction_inst.CreateTable(null,DataBaseLayout,CreateDatabaseLayout,CreateDatabaseLayout);
                }
                else if(TABLECOUNT == 2)
                {
                    // update table structure (for people with older database versions)
                    var LIST_OF_NEW_COLUMNS_SINCE_LAST_UPDATE:Array = ["STATUS","SPEND"];
                    for each(var COLUMN:String in LIST_OF_NEW_COLUMNS_SINCE_LAST_UPDATE)
                    {
                        air_sqllite_interaction_inst.AddColumn(null,"TASKS",COLUMN,"TEXT");
                    }
                    LIST_OF_NEW_COLUMNS_SINCE_LAST_UPDATE = ["STATUS"];
                    for each(var COLUMN:String in LIST_OF_NEW_COLUMNS_SINCE_LAST_UPDATE)
                    {
                        air_sqllite_interaction_inst.AddColumn(null,"TIMES",COLUMN,"TEXT");
                    }
                    
                    LOAD_STUFF();
                }
                else if(TABLECOUNT == 3)
                {
                }
                else if(TABLECOUNT == 4)
                {
                }
                TABLECOUNT++;
            }

            public function FOCUS_IN_input(event:FocusEvent):void
            {
                selectedInput = event.target["parent"];
            }
            
            public function KEYDOWN_input(event:KeyboardEvent):void
            {
                if(event.keyCode == 13)
                {
                    AddTask();
                }
            }
            
            public function KEYDOWN_comment(event:KeyboardEvent):void
            {
                if(event.ctrlKey && (event.keyCode == 13))
                {
                    AddUpdateComment(null,COMMENT.text);
                }
            }

            public function KEYDOWN_status(event:KeyboardEvent):void
            {
                if(event.keyCode == 13)
                {
                    AddStatus();
                }
            }

            public function AddDataGrid(event:Event = null,HeaderText:String = "TASK"):DataGrid2
            {
                /** build tasks iterate through array tasks and  **/
                var DATAGRID2:DataGrid2 = new DataGrid2();
                DATAGRID2.dataGrid.dataProvider = new Array();
                
                var proceed:Boolean = true;
                // o 17. check that the user can not add more than 1 empty datagrid/column
                
                var LAST_DataGrid:DataGrid2 = null;
                try
                {
                    LAST_DataGrid = taskGrids.getChildAt(taskGrids.getChildren().length-1) as DataGrid2;
                    
                    if(c_OBJECT_inst.empty(LAST_DataGrid.dataGrid.dataProvider["source"]))
                    {
                        if(DO_LOAD_STUFF == false)
                        {
                            Alert.show("By adding more than one empty column at a time.","Sorry master. please don't confuse me.");
                            proceed = false;
                        }
                    }
                }
                catch(e:Error)
                {
                    // there is probably only one or none datagrid yet
                }
                
                if(proceed)
                {
                    //// selectedDatagrid = DATAGRID2;
                    
                    DATAGRID2.addEventListener(MouseEvent.CLICK, datagrid_click);
                    
                    if(c_OBJECT_inst.not_empty(HeaderText))
                    {
                        var COLUMN:DataGridColumn = DATAGRID2.dataGrid.columns[0] as DataGridColumn;
                        COLUMN.headerText = HeaderText;
                        COLUMN.dataField = HeaderText;
                    }
                    
                    if(HeaderText == "TASK")
                    {
                        taskGrids.addChild(DATAGRID2);
                        
                        var INPUT:TextInput = new TextInput();
                        INPUT.width = 100;
                        INPUT.addEventListener(FocusEvent.FOCUS_IN, FOCUS_IN_input);
                        INPUT.addEventListener(KeyboardEvent.KEY_DOWN, KEYDOWN_input);
                        INPUT.toolTip = "Hit Enter to add a task.";
                        inputs.addChild(INPUT);
                    }
                    
                    /*                     if(HeaderText == "TIME")
                    {
                    var DGCOLUMN:DataGridColumn = DATAGRID2.dataGrid.columns[0] as DataGridColumn;
                    DGCOLUMN.setStyle("fontSize","9");
                    timeGrid.addChild(DATAGRID2);
                    }
                */                }
                
                return DATAGRID2;
            }

            // public var timeGrid:DataGrid2 = new DataGrid2();

            /** build tasks iterate through array tasks and  **/
             public function AddTimeGrid():void
            {
                var COLUMN:DataGridColumn = timeGrid.dataGrid.columns[0] as DataGridColumn;
                COLUMN.headerText = "TIMES";
                COLUMN.dataField = "TIME";
                COLUMN.setStyle("fontSize",10);
                
                COLUMN.width = 150;
            }
            /** sync datagrid and input textfields scroll **/ 
            public function SyncScroll(event:ScrollEvent):void
            {
                inputs.horizontalScrollPosition = event.position;
                taskGrids.horizontalScrollPosition = event.position;
            }
            
            public function CalcEstimate():void
            {
                DATE_ESTIMATED = new Date();
/*

                var MSSECONDS:Number = DAYS.value * 24 * 60 * 60 * 1000;
                // days    * 24 * minutes * seconds * 1000
                MSSECONDS = MSSECONDS + (HOURS.value * 60 * 60 * 1000);
                // hours * minutes * seconds * 1000
                MSSECONDS = MSSECONDS + (MINUTES.value * 60 * 1000);
                // minutes * seconds * 1000
                DATE_ESTIMATED.setTime(MSSECONDS+DATE_ESTIMATED.getTime());

                var DIFF_START_AKTUELL:Number = new Date().getTime() - DATE_START.getTime();
                var DIFF_START_ESTIMATE:Number = DATE_ESTIMATED.getTime() - DATE_START.getTime();

                var DIFF:Number = DIFF_START_ESTIMATE - DIFF_START_AKTUELL;

                // is not at all correct.... concentration low
                // TIME_LEFT.text = c_DATE_inst.SecondsToYearMonthDayHoursMinutesSeconds( (DIFF*1000) );
 */            }

            /** displays a status in a given displayobject with property ["text"] for a given amount of senconds
             * the idea is that it fades in
             * and out in 3 seconds
             * **/
            var DISPLAYTIMER:Timer = null;
            public function DisplayStatus(event:Event = null,STATUS:String = "",DISPLAYOBJECT:DisplayObject = null,SECONDS:int = 1):void
            {
                if(event == null)
                {
                    if(DISPLAYOBJECT.hasOwnProperty("text"))
                    {
                        if(DISPLAYTIMER != null)
                        {
                            DISPLAYTIMER.stop();
                        }
                        
                        DISPLAYOBJECT.visible = false;
                        DISPLAYOBJECT["text"] = STATUS;
    
                        var EFX_FADE_IN:Fade = new Fade(DISPLAYOBJECT);
                        EFX_FADE_IN.alphaFrom = 0;
                        EFX_FADE_IN.alphaTo = 1;
                        EFX_FADE_IN.duration = SECONDS*1000/2;
    
                        var EFX_FADE_OUT:Fade = new Fade(DISPLAYOBJECT);
                        EFX_FADE_OUT.alphaFrom = 1;
                        EFX_FADE_OUT.alphaTo = 0;
                        EFX_FADE_OUT.duration = SECONDS*1000/2;
    
                        var SEQUENCE:Sequence = new Sequence();
    
                        SEQUENCE.addChild(EFX_FADE_IN);
                        SEQUENCE.addChild(EFX_FADE_OUT);
                        
                        SEQUENCE.end();
                        SEQUENCE.play();
                        SEQUENCE.addEventListener(EffectEvent.EFFECT_END, DisplayStatus);
    
                        /*
                        TIMER = new Timer(1000,SECONDS);
                        TIMER.addEventListener(TimerEvent.TIMER, DisplayStatus);
                        TIMER.addEventListener(TimerEvent.TIMER_COMPLETE, DisplayStatus);
                        TIMER.start();
                         */                
                    }
                }
                else if(event.type == EffectEvent.EFFECT_END)
                {
                    trace(event);
                }
            }

            /** marks a task as e.g. "complete" 
             *     ok -4. button: add status
                user can define his own status
                or select from predefined values: as
                "completed" -> sets status of a task to complete -> background of task will be darkened. a checkbox in preferences allows hiding/showing of completed tasks and also setting the background color individually?
                "billed" -> task was put on a bill and send to the customer

                 ok when a status is put to a top-level task, it automatically needs to be put to all sub-level tasks and times.
             * **/
            public function AddStatus():void
            {
                var DATAPROVIDER:Array = STATUS.dataProvider.toArray();
                
                // is it a new status?
                if(!c_ARRAY_inst.IN_ARRAY(DATAPROVIDER,STATUS.textInput.text,'boolean'))
                {
                    // add it to the collection
                    DATAPROVIDER.push(STATUS.textInput.text);
                    // STATUS.dataProvider = new ArrayCollection(DATAPROVIDER);
                    STATUS.selectedIndex = STATUS.dataProvider.length-1;
                    STATUS.selectedItem = DATAPROVIDER[DATAPROVIDER.length-1];
                    STATUS.validateProperties();
                }

                
                // o when a status is put to a top-level task, it automatically needs to be put to all sub-level tasks and times.
                if(selectedDatagrid.dataGrid.selectedItem != null)
                {
                    // update status of currently selected item

                    ITEMS_TO_UPDATE = new Array();
                    
                    selectedDatagrid.dataGrid.selectedItem["STATUS"] = STATUS.textInput.text;
                    
                    ITEMS_TO_UPDATE = findChildTasks(selectedDatagrid.dataGrid.selectedItem);
                    ITEMS_TO_UPDATE.push(selectedDatagrid.dataGrid.selectedItem);
                    
                    for each(var TASK:Object in ITEMS_TO_UPDATE)
                    {
                        CURRENT_TABLE_NAME = "TASKS";
                        TASK["STATUS"] = STATUS.textInput.text; 
                    }
                    
                    UPDATE_MULTIPLE_ITEMS(null,ITEMS_TO_UPDATE,"TASKS");
                }
            }
            
            var ITEMS_TO_UPDATE:Array = new Array();

            /** updates multipe items tasks or times **/
            public function UPDATE_MULTIPLE_ITEMS(event:Event = null,ITEMS:Array = null,TABLE:String = "",whenFinished:Function = null):void
            {
                if(event == null)
                {
                    if(c_OBJECT_inst.not_empty(ITEMS) && c_OBJECT_inst.not_empty(TABLE))
                    {
                        ITEMS_TO_UPDATE = ITEMS;
                        
                        CURRENT_TABLE_NAME = TABLE;
                        var LAST_ITEM:Object = ITEMS_TO_UPDATE.pop();
                        delete LAST_ITEM["mx_internal_uid"];
                        if(LAST_ITEM.hasOwnProperty("hide"))
                        {
                            delete LAST_ITEM["hide"];
                        }
                        air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,LAST_ITEM,LAST_ITEM["id"],UPDATE_MULTIPLE_ITEMS,UPDATE_MULTIPLE_ITEMS);
                    }
                }
                else
                {
                    if(ITEMS_TO_UPDATE.length > 1)
                    {
                        var LAST_ITEM:Object = ITEMS_TO_UPDATE.pop();
                        delete LAST_ITEM["mx_internal_uid"];
                        if(LAST_ITEM.hasOwnProperty("hide"))
                        {
                            delete LAST_ITEM["hide"];
                        }

                        air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,LAST_ITEM,LAST_ITEM["id"],UPDATE_MULTIPLE_ITEMS,UPDATE_MULTIPLE_ITEMS);
                    }
                    else
                    {
                        var LAST_ITEM:Object = ITEMS_TO_UPDATE.pop();
                        delete LAST_ITEM["mx_internal_uid"];
                        if(LAST_ITEM.hasOwnProperty("hide"))
                        {
                            delete LAST_ITEM["hide"];
                        }

                        air_sqllite_interaction_inst.UPDATE(null,CURRENT_TABLE_NAME,LAST_ITEM,LAST_ITEM["id"],readTableTasksAndTimes,readTableTasksAndTimes);
                    }
                }
            }

            /** when application exits, always save the last started time-tracking record **/
            public function onExit(event:Event = null):void
            {
                if(selectedDatagrid != null)
                {
                    // if status has changed, save it
                    if(selectedDatagrid.dataGrid.selectedItem != null)
                    {
                        if(selectedDatagrid.dataGrid.selectedItem["STATUS"] != STATUS.textInput.text)
                        {
                            AddStatus();
                        }
                    }
    
                    if(TIMER != null)
                    {
                        if(TIMER.running)
                        {
                            // stops timer and adds a record
                            StopTimer();
                        }
                    }
                }
            }
        ]]>
    </fx:Script>
</s:WindowedApplication>