<?xml version="1.0" encoding="utf-8"?>
<s:List xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/mx"
        itemRenderer="de.dtele.ui.components.ResourceItemRenderer"
        allowMultipleSelection="true"
        creationComplete="onCreationComplete(event)"
        keyDown="onKeyDown(event)"
        dragEnabled="true"
        dragMoveEnabled="true"
        dropEnabled="true"
        dragEnter="onDragEnter(event)"
        dragOver="onDragOver(event)"
        dragDrop="onDragDrop(event)"
        dragComplete="onDragComplete(event)">
  
  <fx:Metadata>
    /**
     * A list of resources
     * 
     * @author Mathias Brodala
     */
    
    /**
     * Dispatched when a resource was selected
     */
    [Event("resourceSelected", type="de.dtele.ui.components.events.ResourceListEvent")]
  </fx:Metadata>
  
  <fx:Declarations>
    <mx:Sort id="resourcesSort" compareFunction="compareResources"/>
  </fx:Declarations>
  
  <fx:Script>
    <![CDATA[
      import de.dtele.control.MediaManager;
      import de.dtele.data.IResource;
      import de.dtele.net.MediaRequest;
      import de.dtele.ui.components.events.ResourceListEvent;
      
      import mx.collections.ListCollectionView;
      import mx.collections.Sort;
      import mx.core.DragSource;
      import mx.core.IUIComponent;
      import mx.events.DragEvent;
      import mx.events.FlexEvent;
      import mx.managers.DragManager;
      
      /**
       * Tells whether a resource does not belong to the currently selected source
       * 
       * @param resource The resource to check
       * @return TRUE if the resource belongs to the currently selected source, FALSE otherwise
       */
      protected function isFromOtherSource(resource:IResource, ...a):Boolean {
        
        return resource.source != MediaManager.instance.selectedSource;
      }
      
      /**
       * Compares numerical indices
       */
      protected function compareIndices(a:int, b:int):int {
        
        return a - b;
      }
      
      /**
       * Compares resources by the current main property
       */
      protected function compareResources(a:IResource, b:IResource, fields:Array = null):int {
        
        if (!a || !b) {
          
          return 0;
        }
        
        var aTitle:String = a.properties.title.toLowerCase();
        var bTitle:String = b.properties.title.toLowerCase();
        var result:int = aTitle.localeCompare(bTitle);
        
        // Clamp return value of String.localeCompare() to [-1, 1], to avoid an endless loop
        // @see http://tech.groups.yahoo.com/group/flexcoders/message/84186#ymg0
        return Math.max(-1, Math.min(result, 1));
      }
      
      /**
       * Retrieves the currently selected resources
       * 
       * @see spark.components.List#copySelectedItemsForDragDrop()
       */
      protected function getSelectedResources():Vector.<IResource> {
        
        // Copy the vector so that we don't modify the original
        // since selectedIndices returns a reference.
        var draggedIndices:Vector.<int> = selectedIndices.slice(0, this.selectedIndices.length);
        var resources:Vector.<IResource> = new Vector.<IResource>(draggedIndices.length);
        
        // Sort in the order of the data source
        draggedIndices.sort(compareIndices);
        
        for (var i:int = 0; i < draggedIndices.length; ++i) {
          
          resources[i] = this.dataProvider.getItemAt(draggedIndices[i]) as IResource;
        }
        
        return resources;
      }
      
      /**
       * Overrides the default method called for adding
       * drag data and adds a typed list of resources.
       * 
       * By default a similar data format called "itemsByIndex" is added
       * but this demonstrates how to set specific data.
       */
      public override function addDragData(dragSource:DragSource):void {
        
        dragSource.addHandler(this.getSelectedResources, "resources");
      }
      
      /**
       * Prepares watching data provider changes
       */
      protected function onCreationComplete(event:FlexEvent):void {
        
        this.addEventListener("dataProviderChanged", this.onDataProviderChanged);
        // Fake event to trigger sorting
        this.dispatchEvent(new Event("dataProviderChanged"));
      }
      
      /**
       * Assigns the sorting and refreshes the data source
       */
      protected function onDataProviderChanged(e:Event):void {
        
        if (this.dataProvider) {
        
          var listCollection:ListCollectionView = this.dataProvider as ListCollectionView;
            
          listCollection.sort = this.resourcesSort;
          listCollection.refresh();
        }
      }
      
      /**
       * Assigns the sorting and refreshes the data source
       */
      protected function onKeyDown(event:KeyboardEvent):void {
        
        if (this.selectedItems.length > 0) {
        
          // The first selected resource is taken into account 
          var resource:IResource = this.selectedItems[this.selectedItems.length - 1] as IResource;
          
          MediaManager.instance.selectedResource = resource;
        }
      }
      
      /**
       * Checks if adding of resources is allowed with the currently selected
       * source and whether all resources to add are from a different source.
       * 
       * If true accepts the drop and shows feedback.
       *
       * @param e The DragEvent object.
       */
      protected function onDragEnter(e:DragEvent):void {
        
        e.preventDefault();
        
        if (e.dragSource.hasFormat("resources")) {
        
          var resources:Vector.<IResource> = e.dragSource.dataForFormat("resources") as Vector.<IResource>;
          
          if ((MediaRequest.ADD in MediaManager.instance.selectedSource.allowed) &&
              resources.every(isFromOtherSource)) {
        
            DragManager.acceptDragDrop(e.currentTarget as IUIComponent);
            DragManager.showFeedback(e.ctrlKey ? DragManager.MOVE : DragManager.COPY);
          } else {
            
            DragManager.acceptDragDrop(e.dragInitiator);
            DragManager.showFeedback(DragManager.NONE);
          }
        }
      }
      
      /**
       * Draws the focus indicator while data is being dragged
       * over the list and updates the feedback during drag
       *
       * @param e The DragEvent object.
       */
      protected function onDragOver(e:DragEvent):void {
        
        e.preventDefault();
        
        if (e.dragSource.hasFormat("resources")) {
          
          var resources:Vector.<IResource> = e.dragSource.dataForFormat("resources") as Vector.<IResource>;
          
          if ((MediaRequest.ADD in MediaManager.instance.selectedSource.allowed) &&
            resources.every(isFromOtherSource)) {
            
            this.drawFocus(true);
            
            DragManager.acceptDragDrop(e.currentTarget as IUIComponent);
            DragManager.showFeedback(e.ctrlKey ? DragManager.MOVE : DragManager.COPY);
          } else {
            
            DragManager.acceptDragDrop(e.dragInitiator);
            DragManager.showFeedback(DragManager.NONE);
          }
        }
      }
      
      /**
       * Prevents the default action which removes items from their
       * data sources as this depends on whether moving or copying
       * resources actually succeeded
       *
       * @param e The DragEvent object.
       */
      protected function onDragDrop(e:DragEvent):void {
        
        this.drawFocus(false);
        
        e.preventDefault();
      }

      /**
       * Prevents the default action which removes items from their
       * data sources as this depends on whether moving or copying
       * resources actually succeeded
       *
       * @param e The DragEvent object.
       */
      protected function onDragComplete(e:DragEvent):void {
        
        e.preventDefault();
        
        if (e.dragSource.hasFormat("resources")) {
          
          var resources:Vector.<IResource> = e.dragSource.dataForFormat("resources") as Vector.<IResource>;
          
          switch (e.action) {
            
            case DragManager.COPY:
              
              var resourcesArray:Array = new Array(resources.length);
              
              for (var i:int = 0; i < resources.length; ++i) {
                
                resourcesArray[i] = resources[i];
              }
              
              MediaManager.instance.addResources(resourcesArray, MediaManager.instance.selectedSource);
              break;
            
            case DragManager.MOVE:
              
              MediaManager.instance.moveResources(resources, MediaManager.instance.selectedSource);
              break;
          }
        }
      }
      
      protected function onResourceSelected(e:ResourceListEvent):void {
        
        this.dispatchEvent(e);
      }

    ]]>
  </fx:Script>
  
  <s:layout>
    <s:TileLayout horizontalGap="6" verticalGap="6"/>
  </s:layout>
  
</s:List>