<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:salesforce="http://www.salesforce.com/"
    xmlns:flexlib="http://code.google.com/p/flexlib/"
    xmlns:view="view.*"
    layout="absolute"
    applicationComplete="handleApplicationComplete(event)"
    backgroundColor="#FFFFFF"
    backgroundGradientColors="[#FFFFFF,#F3F3F3]"
    width="100%"
    height="100%"
    minHeight="500"
    minWidth="700"
    pageTitle="SalesForce Account Detail Demo"
    viewSourceURL="srcview/index.html"
    horizontalScrollPolicy="off"
    verticalScrollPolicy="off"
    >
     
    <mx:Style source="./assets/Style.css" />
    
    <mx:Script>
        <![CDATA[
            import vo.StageSetVO;
            import model.Model;
            import constants.ViewIndexes;
            import vo.AccountVO;
            import constants.QueryConstants;
            import mx.utils.StringUtil;
            import mx.rpc.AsyncRequest;
            import com.salesforce.objects.SObject;
            import mx.events.CollectionEvent;
            import mx.events.DataGridEvent;
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.controls.DataGrid;
            import vo.OpportunityVO;
            import mx.collections.ArrayCollection;
            import com.salesforce.results.QueryResult;
            import mx.utils.ObjectUtil;
            import mx.controls.Alert;
            import com.salesforce.AsyncResponder;
            import com.salesforce.objects.LoginRequest;
            import utils.ParsingUtils;
            
            
            [Bindable]
            /**
             * the model is a holder for variables we need to share 
             * amongst other components... its an easy way instead of passing
             * all the variables through component to component, the model
             * lets us easily update one place, and get all the information
             * we need.
             **/
            private var _model:Model = Model.getInstance();
            
            /**
             * the login function gets called from the <mx:Application /> tag
             * 
             * from the event "applicationComplete", this tells the program to run this 
             * function on applicationComplete.
             * 
             * you'll notice in the function that we setup a "callback" that callback is what is 
             * called when the result of the function comes back, whether its a good result from 
             * salesforce or a bad one... (example: a bad login attempt), we do this with the class
             * AsyncResponder... its really not that complicated, its simple, all it is, is what 
             * gets called when we get data back from the salesforce, it takes 2 arguments, 
             * one is a result function, another is a fault function, the fault gets called when
             * the call is bad, and fails, the result is called when its good... 
             *
             **/
            private function login():void {
                _model.apex.protocol = 'http';
                var lr:LoginRequest = new LoginRequest();
                
                if( _model.server_url )
                    _model.apex.serverUrl = _model.server_url;

                if( !_model.session_id )
                {
                    lr.username = 'yourUserName@yahoo.com';
                    lr.password    = 'passwordAndSecurityKey'
                }
                else
                {
                    trace( '[session_id]' + _model.session_id );
                    lr.session_id = _model.session_id;
                    lr.server_url = _model.server_url;
                }
                
                lr.callback    = new AsyncResponder(handleLoadData, handleFault);
                _model.apex.login(lr);
            }
            
            /**
             * handleApplicationComplete is called on applicationComplete, we will
             * use this to get the variables passed into the application
             **/
            private function handleApplicationComplete( event:Event ):void
            {
                var o:Object = Application.application.parameters;
                
                //here we grab a session_id that we passed in as a flashVar
                if( o.hasOwnProperty('session_id') && StringUtil.trim(o.session_id) )
                {
                    _model.session_id = o.session_id;
                }
                
                //here we grab a server_url that we passed in as a flashVar
                if( o.hasOwnProperty('server_url') && StringUtil.trim(o.server_url) )
                {
                    _model.server_url = o.server_url;
                }
                
                //now we grab the account_id as well
                if( o.hasOwnProperty('account_id') && StringUtil.trim(o.account_id) )
                {
                    _model.account_id = o.account_id;
                }
                else
                {
                    _model.account_id = '0018000000R7uwB';
                }
                login();
            }
            
            /**
             * handleFault gets called from the login() function
             * and is called because we setup a callback to call it when 
             * the call fails... see the line that has lr.callback = new AsyncResponder( loadData, handleFault );
             * 
             * we use this to simply let us know it failed, this isn't something you would usually show
             * to the user, but as a programmer, we like to see the error popup at us while we are programming
             * to know something failed... so we setup an Alert to do that by calling Alert.show( string );
             **/            
            private function handleFault(fault:Object):void 
            {
                //the fault is an object, so we use the ObjectUtil class to turn it into a string.
                //Alert.show( ObjectUtil.toString(fault) );
                trace( ObjectUtil.toString(fault) );
            }
            
            /**
             * handleLoadData is called from our callback that is setup in the login() function as well
             * the same as handleFault, this is called when the request is good from the login function, as 
             * long as its successful, we then query salesforce to get our data, 
             * 
             * we also setup a new AsyncResponder to call a function when the data returns
             **/
            private function handleLoadData(lr:Object):void 
            {
                var sAccountById:String = QueryConstants.ACCOUNT_OPPORTUNITIES_BY_ID;
                var replacement:String = "'" + _model.account_id + "'";
                sAccountById = ParsingUtils.stringReplaceAll( sAccountById,'@ID',replacement );
                _model.apex.query( sAccountById, new AsyncResponder( handleAccountResult,handleFault) );
            }
            
            /**
             * handleAccountResult gets called when we return data from our query
             * we use this to parse the data and convert it to classes that our 
             * application understands... we could use generic objects that are 
             * returned from the query, but it's a lot nicer to use vo's or ValueObjects
             * because as a programmer, it allows us to get code hints for properties 
             * in the object 
             **/
            private function handleAccountResult( event:QueryResult ):void
            {
                if (event.size > 0)
                {
                    //setup our acAccounts, even though we are only returning one account,
                    //we'll set it up anyway, we may use it if we want to ever use this application
                    //to add accounts 
                    _model.acAccounts.source = ParsingUtils.convertToArrayCollection( event.records.source,vo.AccountVO ).source;
                    
                    //make the account we are viewing the _model.accountVO
                    //because we are getting the item out of a generic array collection
                    //the compiler will through an error barking at you, saying that 
                    //getItemAt returns a generic obect, not an AccountVO... so we just 
                    //cast it by saying "as AccountVO" which tells the compiler to shut up
                    _model.accountVO = _model.acAccounts.getItemAt(0) as AccountVO;
                    
                    //because our query returns our opportunities as joined objects we have to dig into the object
                    //that is returned in order to get the records we want back out
                    _model.acOpportunities.source = ParsingUtils.convertToArrayCollection( event.records[0].Opportunities.records.source,vo.OpportunityVO ).source;
                    
                    //create an object to map the names that we've already covered
                    var stageNames:Object = {};
                    
                    for( var i:int = 0; i < _model.acOpportunities.length; i++ )
                    {
                        var tempVO:OpportunityVO = _model.acOpportunities.getItemAt(i) as OpportunityVO;
                        var stageSetVO:StageSetVO;
                        //if the stageName isn't found in the object of stageNames, then 
                        //we create it, and create a StageSetVO
                        if( !stageNames.hasOwnProperty(tempVO.StageName) )
                        {
                            stageSetVO = StageSetVO.buildObject();
                            stageSetVO.StageName = tempVO.StageName;
                            
                            //here we setup a key in the object of that stagename that
                            //way we know that we've created that stage
                            stageNames[tempVO.StageName] = stageSetVO;
                            
                            stageSetVO.acOpportunities.addItem( tempVO );
                        }
                        else
                        {
                            stageSetVO = stageNames[tempVO.StageName];
                            
                            stageSetVO.acOpportunities.addItem( tempVO );
                        }
                    }//end loop
                    
                    //now we need to loop over those keys and create our arraycollection
                    //in the model that we can use.
                    for each( var stageSet:StageSetVO in stageNames )
                    {
                        _model.acStageSets.addItem( stageSet );
                    }
                }//end if (event.size)
            }//end function
            
            /**
             * handleSaveAll is called when we click the btnSaveAll
             * 
             * when we edit each item in the datagrid, we store those edited items 
             * in an array to be ran through an updated
             **/
            private function handleSaveAll():void
            {
                trace('save all');
                //first we setup an empty array that will be used to hold 
                //an array of sobjects
                var a:Array = [];
                
                //we now have to loop through the acDirtyOpportunities arraycollection, and 
                //convert them into generic objects that salesforce can save
                for( var i:int = 0; i < _model.acDirtyOpportunities.length; i++ )
                {
                    var tempVO:OpportunityVO = _model.acDirtyOpportunities.getItemAt(i) as OpportunityVO;
                    
                    //we have to create the array of sobjects because thats what the 
                    //salesforce toolkit for flex expects us to do.
                    var o:Object = tempVO.toObject();
                    var so:SObject = new SObject(o);
                    
                    a.push( so );
                }
                
                _model.apex.update( a,new AsyncResponder(handleSaveAllResult,handleFault) );
            } 
            
            /**
             * handleSaveAllResult is called from handleSaveAll, we created an AsyncResponder
             * in the class that gets called when the data is returned from salesforce
             **/
            private function handleSaveAllResult( event:* ):void
            {
                Alert.show( 'All items saved successfully');
            } 
            
            
            
        ]]>
    </mx:Script>
    
    <mx:ApplicationControlBar
        left="5"
        right="5"
        top="5"
        height="80"
        >
        <mx:Image 
            source="@Embed(source='./assets/images/mmlogo.gif')" 
            />
        <mx:HBox
            width="50%"
            height="30"
            >
            <mx:Spacer width="80%" />
            <mx:Label text="Double click to edit, or edit Probabilty and Amount on the fly, click on the chart to drill down" />
            <mx:Button
                id="btnSaveAll"
                label="Save All"
                click="handleSaveAll()"
                />
        </mx:HBox>
    </mx:ApplicationControlBar>
    
    <mx:Parallel id="showEffect">
        <mx:Dissolve />
        <mx:Blur />
    </mx:Parallel>
    <mx:Parallel id="hideEffect">
        <mx:Blur />
        <mx:Dissolve />
    </mx:Parallel>
    
    <mx:DateFormatter
        id="dateFormatter"
        formatString="MM/DD/YYYY"
        />
    <mx:VBox    
        left="5"
        right="5"
        top="100"
        bottom="0"
        horizontalScrollPolicy="off"
        verticalScrollPolicy="off"
        >
        
        <mx:HDividedBox
            width="100%"
            height="100%"
            >
            <view:AccountView
                width="350"
                height="100%"
                accountVO="{_model.accountVO}"
                minWidth="350"
                />
            <mx:VBox
                width="40%"
                height="100%"
                minWidth="200"
                >
                <flexlib:WindowShade
                    width="100%"
                    label="Opportunities"
                    resizeEffect="Resize"
                    >
                    <view:Opportunities
                        width="100%"
                        height="150"
                        />
                </flexlib:WindowShade>
                <flexlib:WindowShade
                    width="100%"
                    label="Contacts"
                    opened="false"
                    resizeEffect="Resize"
                    >
                    <mx:Label text="Nothing in here, but you can see how it would grow" width="100%"/>
                </flexlib:WindowShade>
                <flexlib:WindowShade
                    width="100%"
                    label="Case"
                    opened="false"
                    resizeEffect="Resize"
                    >
                    <mx:Label text="Nothing in here, but you can see how it would grow" width="100%"/>
                </flexlib:WindowShade>
                <flexlib:WindowShade
                    width="100%"
                    label="Opened Activities"
                    opened="false"
                    resizeEffect="Resize"
                    >
                    <mx:Label text="Nothing in here, but you can see how it would grow" width="100%"/>
                </flexlib:WindowShade>
                <flexlib:WindowShade
                    width="100%"
                    label="Notes and Attachments"
                    opened="false"
                    resizeEffect="Resize"
                    >
                    <mx:Label text="Nothing in here, but you can see how it would grow" width="100%"/>
                </flexlib:WindowShade>
            </mx:VBox>
            <mx:ViewStack
                width="60%"
                height="100%"
                selectedIndex="{_model.idxPodSelectedIndex}"
                minWidth="300"
                creationPolicy="all"
                >
                <view:PodContainer
                    width="100%"
                    height="100%"
                    showEffect="{showEffect}"
                    hideEffect="{hideEffect}"
                    />
                <view:OpportunityForm
                    id="opportunityForm"
                    width="100%"
                    height="100%"
                    showEffect="{showEffect}"
                    hideEffect="{hideEffect}"
                    includeInLayout="false"
                    show="btnSaveAll.visible=false"
                    hide="btnSaveAll.visible=true"
                    opportunityVO="{_model.selectedOpportunity}"
                    />
            </mx:ViewStack>
        </mx:HDividedBox>
        <mx:Spacer height="10"/>
    </mx:VBox>
    
</mx:Application>