/*
* Copyright 2003-2010 Tufts University  Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 * 
 * http://www.osedu.org/licenses/ECL-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

/*
 * TextOpenAction.java
 * 
 * Very rough initial data import demos and first Zotero Prototype
 * data import
 *
 * Created on October 23, 2003, 12:40 PM
 */

package tufts.vue.action;

/**
 *
 * @author  akumar03
 * @author  dhelle01
 */
import java.io.*;
import java.net.*;
import java.util.zip.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import tufts.vue.*;
 
public class TextOpenAction  extends VueAction {
    
    private static boolean DEBUG_LOCAL = false;
    
    //todo: add constants for row length, starting position, y gaps and x gaps
    //also make these all be read from VueResources.properties file for ease of modification
    //after compilation
    public static final int NODE_LABEL_TRUNCATE_LENGTH = 8;
    public static final int CIRCLE_LAYOUT = 0;
    public static final int RANDOM_LAYOUT = 1;
    public static final int GRAVITY_LAYOUT = 2;
    public static final int STAGGERED_LAYOUT = 3;
    public static int layout = STAGGERED_LAYOUT;
    public static final int MAP_SIZE = 500;
    public static final int MAX_SIZE =5000;
    
    public static boolean ZOTERO_PROTOTYPE = false;
    
    public TextOpenAction(String label) {
        super(label, null, ":general/Open");
    }
    
    public TextOpenAction() {
        this("Import Text file...");
    }
    
    public static boolean isZoteroProtypeEnabled()
    {
    	return ZOTERO_PROTOTYPE;
    }
    
    public static void setZoteroPrototypeEnabled(boolean enabled)
    {
    	ZOTERO_PROTOTYPE = enabled;
    }
    
    // workaround for rapid-succession Ctrl-O's which pop multiple open dialogs
    private static final Object LOCK = new Object();
    private static boolean openUnderway = false;
    public void actionPerformed(ActionEvent e) {
        synchronized (LOCK) {
            if (openUnderway)
                return;
            openUnderway = true;
        }
        try {
            File file = ActionUtil.openFile("Open Map", "text");
            displayMap(file);
            System.out.println("Action["+e.getActionCommand()+"] completed.");
        } finally {
            openUnderway = false;
        }
    }
    
    
    public static void displayMap(File file) {
        if (file != null) {
            VUE.activateWaitCursor();
            try {
                LWMap loadedMap = loadMap(file.getAbsolutePath());
                VUE.displayMap(loadedMap);
            }catch(Throwable t) {
                t.printStackTrace();
            } finally {
                VUE.clearWaitCursor();
            }
        }
    }
    
    // todo: have only one root loadMap that hanldes files & urls -- actually, make it ALL url's
    public static LWMap loadMap(String fileName) throws Exception{
        Map<String,LWNode> nodeMap = new HashMap<String,LWNode>();
         Map<String,Integer> repeatMap = new HashMap<String,Integer>();
        Map<String,LWLink> linkMap = new HashMap<String, LWLink>();
        String mapName = fileName.substring(fileName.lastIndexOf("/")+1,fileName.length());
        if(mapName.lastIndexOf(".")>0)
            mapName = mapName.substring(0,mapName.lastIndexOf("."));
        if(mapName.length() == 0)
            mapName = "Text Import";
        LWMap map = new LWMap(mapName);
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
        String line;
        String links = null; // for Zotero prototype
        
        if(!ZOTERO_PROTOTYPE) // in zotero import each row is a node
        {    
          reader.readLine(); // skip the first line
        }
        int count = 0;
        
        // for staggered layout
        float y = 20;
        float x = 0;
        int toggle = 0;
        
        List<String[]> linksList = new ArrayList<String[]>(); // also for Zotero Prototype
        
        while((line=reader.readLine()) != null && count <MAX_SIZE) {
           
            if(DEBUG_LOCAL)
            {    
              System.out.println(line+" words: "+line.split(",").length);
            }
            
            if(ZOTERO_PROTOTYPE)
            {
              String[] parts = line.split("\"");
              //todo: sanity check on number of parts
              line = parts[0];
              
              if(parts.length > 1)
                links = parts[1];
              else
                links = null;
              
              if(DEBUG_LOCAL)
              {    
                if(links!=null)
                {    
                  System.out.println("links -- " + links);
                }
              }
            }    
            
            String[] words = line.split(",");
            LWNode node1;
            LWNode node2 = null;
            
            if(ZOTERO_PROTOTYPE)
            {   
                node1 = new LWNode(words[1]);
                try
                { 
                  // todo: can throw its own stack trace
                  // so, avoid for non standard (rdf, zotero notes etc.) uri;
                  Resource resource = map.getResourceFactory().get(words[2]);
                  node1.setResource(resource);
                }
                catch(Exception e)
                {
                  System.out.println("Exception setting resource: " + e);
                }
                
                String id = words[0];
                
                if(links != null)
                {
                  String[] linksTo = links.split(",");
                
                  String[] arr = new String[linksTo.length+1];
                
                  arr[0] = id;
                
                  System.arraycopy(linksTo,0,arr,1,linksTo.length);
              
                  linksList.add(arr);
                }
                
                // this makes the id editable.. 
                // have to be careful about maintaining id for updates
                // todo: put in content info instead.
                //edu.tufts.vue.metadata.VueMetadataElement vme = new edu.tufts.vue.metadata.VueMetadataElement();
                ////vme.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY);
                //String[] obj = {"http://vue.tufts.edu/custom.rdfs#z_id",id};   
                //vme.setObject(obj);
                //node1.getMetadataList().getMetadata().add(vme);
                //vme.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY);
                
                // put in content info instead of keywords:
                node1.getResource().setProperty("Zotero.id",id);
                
                // just for debug purposes -- many more references than I expected
                // and they are not bidirectional.. this seems to not just be
                // the "related" relation from zotero, which is probably what we want?
                //edu.tufts.vue.metadata.VueMetadataElement vme2 = new edu.tufts.vue.metadata.VueMetadataElement();
                //vme2.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY);
                //String[] obj2 = {"http://vue.tufts.edu/custom.rdfs#z_links",links};   
                //vme2.setObject(obj2);
                //if(links!=null)
                //node1.getMetadataList().getMetadata().add(vme2);
                
                nodeMap.put(words[0],node1);
                
                if(DEBUG_LOCAL)
                {
                    System.out.println("Node map addition " + words[0] + "," + node1);
                }
                //repeatMap.put(words[0], new Integer(1));
                map.add(node1);
                node1.layout();
            }
            else
            if(words.length == 4) {
                if(!nodeMap.containsKey(words[0])) {
                    node1 = new LWNode(words[0]);
                      Resource resource = map.getResourceFactory().get(words[1]);
                    node1.setResource(resource);
                    nodeMap.put(words[0],node1);
                     repeatMap.put(words[0], new Integer(1));
                    map.add(node1);
                } else {
                    node1 = nodeMap.get(words[0]);
                    int nc= repeatMap.get(words[0]).intValue();
                    repeatMap.put(words[0],new Integer(nc+1));
                }
                if(!nodeMap.containsKey(words[2])) {
                    node2 = new LWNode(words[2]);
                     Resource resource =   map.getResourceFactory().get( words[3] );
                    node2.setResource(resource);
                    map.add(node2);
                    nodeMap.put(words[2],node2);
                    repeatMap.put(words[2], new Integer(1));
                    
                } else {
                    node2 = nodeMap.get(words[2]);
                      int nc= repeatMap.get(words[2]).intValue();
                    repeatMap.put(words[2],new Integer(nc+1));
                }
                
            }else {
                
                try
                {
                
                if(!nodeMap.containsKey(words[0])) {
                    node1 = new LWNode(words[0]);
                    nodeMap.put(words[0],node1);
                     repeatMap.put(words[0], new Integer(1));
                    map.add(node1);
                } else {
                    node1 = nodeMap.get(words[0]);
                      int nc= repeatMap.get(words[0]).intValue();
                    repeatMap.put(words[0],new Integer(nc+1));
                }
                if(!nodeMap.containsKey(words[1])) {
                    node2 = new LWNode(words[1]);
                    map.add(node2);
                    nodeMap.put(words[1],node2);
                     repeatMap.put(words[1], new Integer(1));
                } else {
                    node2 = nodeMap.get(words[1]);
                      int nc= repeatMap.get(words[1]).intValue();
                    repeatMap.put(words[1],new Integer(nc+1));
                }
                
                }
                catch(Exception e)
                {
                    System.out.println("Exception in text import: " + e);
                    node1 = new LWNode("exception");
                    node2 = new LWNode("exception 2");
                }
                
            }
            
            
            if(ZOTERO_PROTOTYPE)
            {
                // actually just avoiding else clause code
                // zotero links are calculated after loop
                // using linkslist and nodemap
            }
            else
            {
                
                
              System.out.println("COUNT: "+count+" Node1 :"+ node1.getLabel()+" Node2 : "+
                      (node2.getLabel()));
            
              String linkKey = node1.getLabel()+node2.getLabel();
              if(!linkMap.containsKey(linkKey)) {
                
                  LWLink link = new LWLink(node1,node2);
                  linkMap.put(linkKey,link);
                
                  map.add(link);
              }
            }
            
            if(layout == STAGGERED_LAYOUT)
            {
                //x += 150;
                x += 300;
                
                if(toggle == 0)
                {
                    toggle++;
                    y = y + 50;
                }
                else
                if(toggle == 1)
                {
                    toggle = 0;
                    y = y - 50;
                }
                if(count % 5 == 0)
                {
                    y += 100;
                    x = 400;
                    toggle = 0;
                }
                
                node1.setLocation(x,y);
                if(node2 != null)
                  node2.setLocation(x+150,y);
            }
            if(layout == CIRCLE_LAYOUT ) {
                double angle = Math.random()*Math.PI*4;
                node1.setLocation(MAP_SIZE*(1+Math.cos(angle)),MAP_SIZE*(1+Math.sin(angle)));
                angle = Math.random()*Math.PI*4;
                node2.setLocation(MAP_SIZE*(1+Math.cos(angle)),MAP_SIZE*(1+Math.sin(angle)));
            } else if (layout == RANDOM_LAYOUT) {
                 node1.setLocation(MAP_SIZE*Math.random(),MAP_SIZE*Math.random());
               node2.setLocation(MAP_SIZE*Math.random(),MAP_SIZE*Math.random());
            } else if(layout == GRAVITY_LAYOUT) {
                  double angle = Math.random()*Math.PI*4;
                  int nc1= repeatMap.get(node1.getLabel()).intValue();
                 // nc1 = nc1*nc1;
                  double fact1 = Math.sqrt(nc1);
                node1.setLocation(MAP_SIZE*(1+Math.cos(angle)/fact1),MAP_SIZE*(1+Math.sin(angle)/fact1));
                angle = Math.random()*Math.PI*4;
                int nc2= repeatMap.get(node1.getLabel()).intValue();
                //nc2=nc2*nc2;
                double fact2 = Math.sqrt(nc2);
                node2.setLocation(MAP_SIZE*(1+Math.cos(angle)/fact2),MAP_SIZE*(1+Math.sin(angle)/fact2));
            }
            
            count++;
        }
        
        if(ZOTERO_PROTOTYPE)
        {
            Iterator<String[]> nodes = linksList.iterator();
            
            while(nodes.hasNext())
            {
                String[] arr = nodes.next();
                LWNode node = nodeMap.get(arr[0]);
                
                if(DEBUG_LOCAL)
                {
                    System.out.println("TextOpenAction: linkslist arr length " + arr.length);
                }
                
                for(int i=1;i<arr.length;i++)
                {
                  String linkKey = arr[0] + "|" +  arr[i];
                  
                  if(DEBUG_LOCAL)
                  {
                      System.out.println("TextOpenAction: linkKey " + linkKey);
                  }
                  
                  if(!linkMap.containsKey(linkKey)) 
                  {
                      
                    if(DEBUG_LOCAL)
                    {
                      System.out.println("TextOpenAction: accepted -- " + linkKey);  
                    }
                      
                    LWNode node2 = nodeMap.get(arr[i]);
                    LWLink link = new LWLink(node,node2);
                    linkMap.put(linkKey,link);
                
                    map.add(link);
                  }
                }
            }
        }
        
        return map;
    }
     
    
    
    public static void main(String args[]) throws Exception {
        String file = args.length == 0 ? "C:\\anoop\\vue\\maps\\Short.rdf" : args[0];
        System.err.println("Attempting to read map from " + file);
        DEBUG.Enabled = true;
        VUE.parseArgs(args);
        LWMap map;
        
        map = loadMap(file);
        System.out.println("Loaded map: " + map);
    }
}
