/*
 * WANDORA
 * Knowledge Extraction, Management, and Publishing Application
 * http://wandora.org
 * 
 * Copyright (C) 2004-2023 Wandora Team
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 
 * PasteAssociations.java
 *
 * Created on 22. joulukuuta 2004, 12:27
 */

package org.wandora.application.tools.associations;




import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.wandora.application.Wandora;
import org.wandora.application.WandoraTool;
import org.wandora.application.contexts.Context;
import org.wandora.application.gui.WandoraOptionPane;
import org.wandora.application.tools.AbstractWandoraTool;
import org.wandora.topicmap.Association;
import org.wandora.topicmap.Locator;
import org.wandora.topicmap.Topic;
import org.wandora.topicmap.TopicMap;
import org.wandora.topicmap.TopicMapException;
import org.wandora.utils.ClipboardBox;
import org.wandora.utils.Textbox;


/**
 * <p>
 * Paste association tool class injects associations to Wandora topics.
 * Associations are generated with clipboard text. Clipboard should contain text
 * in specific format:
 * </p>
 * 
 * <code>
 * ASSOCIATION_TYPE{newline}
 * ROLE_1{tab}ROLE_2{tab}ROLE_3{newline}
 * PLAYER_a1{tab}PLAYER_a2{tab}PLAYER_a3{newline}
 * PLAYER_b1{tab}PLAYER_b2{tab}PLAYER_b3{newline}
 * PLAYER_c1{tab}PLAYER_c2{tab}PLAYER_c3{newline}
 * {newline}
 * </code>
 * <p>
 * where all topic references are base names and {newline} is a newline character
 * and {tab} a tabulator character.
 * Accepted association format is generated by Copy associations tool for example.
 * </p>
 * If role or player does not exist the tool asks user whether or not it should
 * create a new topic for the given base name.
 * <p>
 * Placing special keyword __CURRENT_TOPIC__ as a player of injected text generates
 * associations where player is replaced with a context topic.
 * </p>
 * 
 * @see CopyAssociations
 * 
 * @author  akivela
 */

public class PasteAssociations extends AbstractWandoraTool implements WandoraTool {


	private static final long serialVersionUID = 1L;
	
	private boolean requiresRefresh = false;
    private int userInterrupt;
    private boolean yesToAllCountErrors;
    
    
    
    
    public PasteAssociations(){
    }
    public PasteAssociations(Context preferredContext){
        setContext(preferredContext);        
    }

    @Override
    public String getName() {
        return "Paste association";
    }

    @Override
    public String getDescription() {
        return "Injects clipboard tab text associations to Wandora.";
    }

    @Override
    public boolean requiresRefresh() {
        return requiresRefresh;
    }
    
    
    
    
    
    
    @Override
    public void execute(Wandora wandora, Context context) {
        requiresRefresh = false;
        TopicMap topicMap = wandora.getTopicMap();
        
        Iterator topics = context.getContextObjects();

        Topic topicOpen = null;
        
        if(topics != null && topics.hasNext()) {
            topicOpen = (Topic) topics.next();
            if(topicOpen != null) {
                Association a = null;
                String tabText = ClipboardBox.getClipboard();
                String[] associationGroups = tabText.split("\n\r?\n\r?");

                yesToAllCountErrors = false;
                yesToAllNewTopics = false;
                userInterrupt = 0;

                // AssociationGroup is a collection of associations with same
                // association type and same roles. Tool assumes that association groups
                // are separated with two new line characters (see split above).

                for(int ai=0; ai<associationGroups.length && userInterrupt == 0; ai++) {
                    StringTokenizer st = new StringTokenizer(associationGroups[ai], "\n");
                    try {
                        String aType = null;
                        if(st.hasMoreTokens()) aType = st.nextToken();
                        else { 
                            WandoraOptionPane.showMessageDialog(wandora ,"Clipboard contains no association type! No associations created!","Association type missing!",WandoraOptionPane.ERROR_MESSAGE); 
                            return;
                        }

                        String aRoles = null;
                        if(st.hasMoreTokens()) aRoles = st.nextToken();
                        else { 
                            WandoraOptionPane.showMessageDialog(wandora ,"Clipboard contains no association roles! No associations created!","Association roles missing!",WandoraOptionPane.ERROR_MESSAGE); 
                            return;
                        }
                        List<String> aRoleVector = vectorize(aRoles, "\t");

                        if(!st.hasMoreTokens()) {
                            WandoraOptionPane.showMessageDialog(wandora ,"Clipboard contains no association players! No associations created!","Association players missing!",WandoraOptionPane.ERROR_MESSAGE);
                            return;
                        }

                        int count = 0;
                        int line = 0;
                        boolean hasAssociations = false;

                        while(st.hasMoreTokens() && aType != null && aRoles != null && userInterrupt == 0) {
                            line++;
                            hasAssociations = true;
                            List<String> playerVector = vectorize(st.nextToken(), "\t");
                            if(playerVector.size() == aRoleVector.size()) {
                                List<Topic> aPlayerTopicVector = getTopics(wandora, topicMap, playerVector, topicOpen);
                                List<Topic> aRoleTopicVector = getTopics(wandora, topicMap, aRoleVector, topicOpen);

                                if(userInterrupt == WandoraOptionPane.NO_OPTION) {
                                    // User has disallowed topic creation. Skipping this association.
                                    continue;
                                }
                                else if(userInterrupt == WandoraOptionPane.CANCEL_OPTION) {
                                    // User has disallowed topic creation and requests for cancellation.
                                    break;
                                }

                                a = null;
                                for(int i=0; i<playerVector.size(); i++) {
                                    Topic player = (Topic) aPlayerTopicVector.get(i);
                                    Topic role = (Topic) aRoleTopicVector.get(i);
                                    if(player != null && role != null) {
                                        if(a == null) {
                                            requiresRefresh = true;
                                            Topic at = getTopic(wandora, topicMap, aType);
                                            if(at == null) break;
                                            a = topicMap.createAssociation(at);
                                            count++;
                                        }
                                        a.addPlayer(player, role);
                                    }
                                }
                            }
                            else { 
                                // It seems that the number of players does not equal number of roles.
                                // Checking if user cancels operation!
                                if( !yesToAllCountErrors ) {
                                    int an = WandoraOptionPane.showConfirmDialog(wandora ,"Number of players is not equal to number of roles! " +
                                                                                    "Rejecting association! " +
                                                                                    "Would you like to continue the operation?",
                                                                                    "Player number mismatch number of roles!",
                                                                                    WandoraOptionPane.YES_TO_ALL_NO_OPTION);
                                    if(an == WandoraOptionPane.NO_OPTION) userInterrupt = WandoraOptionPane.NO_OPTION;
                                    else if(an == WandoraOptionPane.YES_TO_ALL_OPTION) yesToAllCountErrors = true;
                                }
                            }
                        }
                        if(hasAssociations && count == 0) {
                            WandoraOptionPane.showMessageDialog(wandora ,"Association creation failed! No associations created!","No associations created!",WandoraOptionPane.ERROR_MESSAGE);
                        }
                    }
                    catch (Exception e) {
                        log("Exception '" + e.toString()+ "' occurred while creating associations!", e);
                        //if(a != null) a.remove();
                    }
                }
            }
            else {
                log("Can't solve open topic!");
            }
        }
        
    }    
    
    
      
    
    public List<String> vectorize(String str, String delim) {
        List<String> v = new ArrayList<>();
        if(str != null && str.length() > 0) {
            StringTokenizer sto = new StringTokenizer(str, delim);
            while(sto.hasMoreTokens()) {
                v.add(sto.nextToken());
            }
        }
        return v;
    }
    
    
    
    private boolean yesToAllNewTopics = false;
    
    public Topic getTopic(Wandora wandora, TopicMap topicMap, String topicName)  throws TopicMapException {
        if(topicName != null) {
            topicName = Textbox.trimExtraSpaces(topicName);
            if(topicName.length() > 0) {
                Topic t = topicMap.getTopicWithBaseName(topicName);
                if(t == null) {
                    // Testing if topicName matches any subject identifiers.
                    t = topicMap.getTopic(topicName);
                }
                if(t == null) {
                    // Testing if topicName matches any subject locators.
                    t = topicMap.getTopicBySubjectLocator(topicName);
                }
                if(t == null) {
                    // Looks like there is no acquired topic available!
                    // Checking if user allows topic creation.
                    if(!yesToAllNewTopics) {
                        String tempTopicName = topicName;
                        if(tempTopicName.length() > 256) tempTopicName = tempTopicName.substring(0,256) + "...";
                        int a = WandoraOptionPane.showConfirmDialog(
                                wandora, 
                                "Topic '"+tempTopicName+"' does not exists! Would you like to create new topic for '"+tempTopicName+"'.", 
                                "Create topic?", 
                                WandoraOptionPane.YES_TO_ALL_NO_CANCEL_OPTION);
                        if( a == WandoraOptionPane.NO_OPTION ) { userInterrupt = a; return null; }
                        if( a == WandoraOptionPane.CANCEL_OPTION ) { userInterrupt = a; return null; }
                        if( a == WandoraOptionPane.YES_TO_ALL_OPTION ) { yesToAllNewTopics = true; }
                    }
                    requiresRefresh = true;
                    t = topicMap.createTopic();
                    if(topicName.startsWith("http://") || topicName.startsWith("https://")) {
                        t.addSubjectIdentifier(new Locator(topicName));
                    }
                    else {
                        t.setBaseName(topicName);
                        String si = "http://wandora.org/si/" + System.currentTimeMillis();
                        int counter = 1000;
                        while(topicMap.getTopic(si) != null && --counter > 0) {
                            si = "http://wandora.org/si/" + System.currentTimeMillis() + counter;
                        }
                        t.addSubjectIdentifier(new Locator(si));
                    }
                }
                return t;
            }
        }
        return null;
    }
   
    
    
    public List<Topic> getTopics(Wandora wandora, TopicMap topicMap, List<String> topicNames, Topic defaultTopic)  throws TopicMapException {
        List<Topic> topics = new ArrayList<>();
        Topic t = null;
        for (String topicName : topicNames) {
            if("__CURRENT_TOPIC__".equals(topicName)) {
                t = defaultTopic;
            }
            else {
                t = getTopic(wandora, topicMap, topicName);
            }
            if(t != null) topics.add(t);
            if(userInterrupt != 0) return null;
        }
        return topics;
    }
    
    
}
