<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:salesforce="http://www.salesforce.com/"
    xmlns:view="view.*"
    layout="absolute"
    applicationComplete="login(event)"
    backgroundColor="#FFFFFF"
    backgroundGradientColors="[#FFFFFF,#F3F3F3]"
    width="700"
    height="500"
    pageTitle="SalesForce Simple Demo"
    viewSourceURL="srcview/index.html"
    horizontalScrollPolicy="off"
    verticalScrollPolicy="off"
    >
    
    <mx:Style source="./assets/Style.css" />
    
    <!--
        setup a Connection, we use a custom namespace here called "salesforce"
        that we setup in the application tag above
        
        xmlns:salesforce="http://www.salesforce.com/"
        
        we are able to do this because we have the as3Salesforce.swc in the libs folder
    -->
    <salesforce:Connection
        id            =    "apex" 
        serverUrl    =    "http://www.salesforce.com/services/Soap/u/14.0"
        protocol    =    "http"
        />
    
    <mx:Script>
        <![CDATA[
            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;
            
            /**
             * you'll notice the [Bindable] tag before the declaration of the variable
             * 
             * this tells our application when its' being compiled to setup listeners for 
             * when the data is changed to automatically show the changes on the interface
             * 
             * arrayCollection's are bindable, by default regular Array's are not... 
             * 
             * think of an ArrayCollection as a wrapper to an array... that just allows the 
             * interface to bind to it... 
             **/
            [Bindable]
            private var acOpportunities:ArrayCollection = new ArrayCollection();
            
            /**
             * we need to hold an array collection of items that we've edited that way we can do a 
             * bulk update when the user clicks "Save All" 
             * 
             * we named this "dirty" because the items it holds need to be sent to salesforce to be updated
             **/
            public var acDirtyOpportunities:ArrayCollection = new ArrayCollection();
            
            
            /**
             * selectedOpportunity is a public variable we will use to be the "editing" opportunity
             * we use this variable to pass to the form that is editing the object it is also bindable
             * 
             * if you look toward the bottom of the code, you'll see the compont <view:OpportunityForm vo="{selectedOpportunit} />
             * 
             * this means that when we set this variable in any function, it will automatically be changed in the that form
             **/
            [Bindable]
            public var selectedOpportunity:OpportunityVO;
            
            /**
             * 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(event:Event):void {
                apex.protocol = 'http';
                var lr:LoginRequest = new LoginRequest();
                lr.username = 'yourusername@yahoo.com';
                lr.password    = 'yourPasswordandSecurityToken'
                lr.callback    = new AsyncResponder(handleLoadData, handleFault);
                apex.login(lr);
            }
            
            /**
             * 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) );
            }
            
            /**
             * 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 
            {
                apex.query( "Select o.StageName, o.Probability, o.Owner.Name, o.OwnerId, o.Name, o.Id, o.Description, o.CloseDate From Opportunity o", new AsyncResponder( handleQueryResult,handleFault) );
            }
            
            /**
             * handleQueryResult 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 handleQueryResult( event:QueryResult ):void
            {
                if (event.size > 0)
                {
                    acOpportunities.source = ParsingUtils.convertToArrayCollection( event.records.source,vo.OpportunityVO ).source;
                }
            }
            
            /**
             * 
             **/
            private function handleOpportunityClick():void
            {
                //if the datagrid has something selected, then we need to 
                //make it the selectedOpportunity, and show the opportunityForm 
                //for the user to edit
                if( opportunityGrid.selectedItem )
                {
                    selectedOpportunity = opportunityGrid.selectedItem as OpportunityVO;
                    opportunityForm.visible = true;
                    opportunityForm.includeInLayout = true;
                }
            }
            
            /**
             * 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 < acDirtyOpportunities.length; i++ )
                {
                    var tempVO:OpportunityVO = 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 );
                }
                
                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');
            } 
            
            /**
             * when the item is done editing in the grid, we need to save that 
             * to the list of items to save when the user clicks "save all"
             **/
            private function handleEditEnd( event:DataGridEvent ):void
            {
                //here we need to grab the item from the acOpportunities arraycollection
                //
                //we do that by getting the row index that is in the event from the datagrid 
                //when it dispatches the itemEditEnd event
                //
                //don't be confused by us creating the tempVO, i'm simply doing this to be able to 
                //get more code hints
                
                var tempVO:OpportunityVO = this.acOpportunities.getItemAt(event.rowIndex) as OpportunityVO;
                
                //now that we have the tempVO, we need to add it to the acDirtyOpportunities
                //we first need to check if it is already in the dirty items, that way we don't add it twice
                if( !acDirtyOpportunities.contains(tempVO) )
                {
                    acDirtyOpportunities.addItem(tempVO);
                }
                  
            } 
            
            private function dateLabelFunction( item:Object,column:DataGridColumn ):String
            {
                var r:String;
                
                r = dateFormatter.format( item[column.dataField] );
                
                return r;
            }
            
        ]]>
    </mx:Script>
    
    <mx:ApplicationControlBar
        left="5"
        right="5"
        top="5"
        height="80"
        >
        <mx:Image 
            source="./assets/images/mmlogo.gif" 
            />
    </mx:ApplicationControlBar>
    
    <mx:DateFormatter
        id="dateFormatter"
        formatString="MM/DD/YYYY"
        />
    <mx:VBox    
        left="5"
        right="5"
        top="100"
        bottom="0"
        horizontalScrollPolicy="off"
        verticalScrollPolicy="off"
        >
        <mx:HBox
            width="100%"
            height="30"
            >
            <mx:Label text="Double click to edit, or edit Probabilty and CloseDate on the fly" />
            <mx:Spacer width="80%" />
            <mx:Button
                id="btnSaveAll"
                label="Save All"
                click="handleSaveAll()"
                />
                
        </mx:HBox>
        <mx:HBox
            width="100%"
            height="100%"
            >
        <mx:DataGrid
            width="100%"
            height="100%"
            id="opportunityGrid"
            dataProvider="{acOpportunities}"
            doubleClickEnabled="true"
            doubleClick="handleOpportunityClick()"
            resizeEffect="Resize"
            editable="true"
            itemEditEnd="handleEditEnd(event)"
            >
            <mx:columns>
                <mx:DataGridColumn 
                    dataField="Name"
                    editable="false" 
                    />
                <mx:DataGridColumn 
                    dataField="OwnerName" 
                    editable="false" 
                    headerText="Owner" 
                    />
                <mx:DataGridColumn 
                    dataField="Probability"
                    editable="true"
                    editorDataField="value"
                    >
                    <mx:itemEditor>
                        <mx:Component>
                            <mx:NumericStepper 
                                minimum="0"
                                maximum="100"
                                />
                        </mx:Component>
                    </mx:itemEditor>
                </mx:DataGridColumn>
                <mx:DataGridColumn 
                    dataField="CloseDate"
                    editable="true"
                    itemEditor="mx.controls.DateField"
                    editorDataField="selectedDate"
                    labelFunction="dateLabelFunction"
                    />
            </mx:columns>
        </mx:DataGrid>
        <view:OpportunityForm
            id="opportunityForm"
            apex="{apex}"
            width="300"
            height="97%"
            visible="false"
            includeInLayout="false"
            show="btnSaveAll.visible=false"
            hide="btnSaveAll.visible=true"
            opportunityVO="{selectedOpportunity}"
            />
        </mx:HBox>
    </mx:VBox>
    
</mx:Application>