/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool
          with fuzzy matching, translation memory, keyword search,
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2014 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT 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.

 OmegaT 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/>.
 **************************************************************************/

package org.omegat.core.matching.external.rmi;

import org.omegat.core.data.PrepareTMXEntry;
import org.omegat.util.TMXProp;

import java.util.*;
import java.rmi.*;

/** 
 * Omega-T RMI implementation, non-project server
 * this server, working on non-GUI mode, simply keeps in RAM what the clients send.
 * Note : no possibility to save anything at server side: once closed, only what the clients have saved will have any importance.
 **/
public class RAMServer extends java.rmi.server.UnicastRemoteObject implements IDistantExternalMemory {
    public static final long serialVersionUID = 1L;

    public RAMServer() throws RemoteException {}    // super() may throw Exception

    public static RAMServer open(String portStr, String object) throws Exception {
        int port = 1099; if (portStr != null) port = Integer.parseInt(portStr);
        java.rmi.registry.LocateRegistry.createRegistry(port);
        System.err.println("Listening server " + port);
        
        if (! object.startsWith("//")) if (port != 1099) object = "//localhost:" + port + "/" + object;
        RAMServer res = new RAMServer(); java.rmi.Naming.bind(object, res);
        System.err.println("Listening server object " + object);
        
        return res;
    }
    
    
    protected Deque<PrepareTMXEntry> changesByDate = new java.util.concurrent.ConcurrentLinkedDeque<>();
    private Map<String,Iterator<PrepareTMXEntry>> openedCursors = new HashMap<>();
    private Map<String,PrepareTMXEntry> openedEntries = new HashMap<>();
    
    /** Opens a new cursor. Method to be sent by the client before others **/
    public String findChanges (long timeStamp) {
        Iterator<PrepareTMXEntry> iter = changesByDate.descendingIterator(); // most recent first!!!
        openedCursors.put(iter.toString() + "#" + timeStamp, iter); return iter.toString() + "#" + timeStamp;
    }
    
    // Next methods retreive entry content 
    // Parameter id must contain result from {findChanges}
    
    public boolean next(String id) throws RemoteException {
        Iterator<PrepareTMXEntry> iter = openedCursors.get(id); if (iter == null) throw new RemoteException("Cursor " + id + " does not exist");
        if (iter.hasNext()) 
            try { 
                PrepareTMXEntry te = iter.next();
                long timeStamp = Long.parseLong(id.substring(id.lastIndexOf('#') + 1));
                if ((te.changeDate > timeStamp) || (te.creationDate > timeStamp)) {
                    openedEntries.put(id, iter.next()); return true;
                }
                // else: end the loop here, so, clean and return false
            } catch (Exception e) {
                org.omegat.util.Log.log(e);
            }
        // If not true, either because iter had not next or because this element was too old
        openedEntries.remove(id); openedCursors.remove(id); return false;
    }
    
    public String getEntrySource(String id) { return openedEntries.get(id).source; }
    public String getEntryTranslation(String id) { return openedEntries.get(id).translation; }    
    public String getEntryAuthor(String id) { return openedEntries.get(id).creator; }        
    public String getEntryLastModifier(String id) { return openedEntries.get(id).changer; }    
    public long getEntryCreationDate(String id) { return openedEntries.get(id).creationDate; }    
    public long getEntryLastModificationDate(String id) { return openedEntries.get(id).changeDate; }    
    public String getEntryNote(String id) { return openedEntries.get(id).note; }    
    
    public String[] getEntryKey(String id) { 
        PrepareTMXEntry en = openedEntries.get(id); List<TMXProp> props = en.otherProperties;
        if (props == null) return null;
        if (props.size() == 0) return null;
        return new String[] { en.getPropValue("file"), getEntrySource(id), 
            en.getPropValue("id"), en.getPropValue("prev"), en.getPropValue("next"), en.getPropValue("path") };
    }    
    
    public void registerDefaultTranslation(String src, String tra) {
        PrepareTMXEntry previous = null;
        // Step 1. Remove similar translation, if any
        for (Iterator<PrepareTMXEntry> iter = changesByDate.iterator(); iter.hasNext(); ) {
            PrepareTMXEntry current = iter.next();
            if (current.source.equals(src) && (current.otherProperties == null)) { 
                iter.remove();
                if (previous == null) previous = current;
            }
        }
        // Step 2. Add 
        if (previous != null) { previous.changeDate = System.currentTimeMillis(); previous.translation = tra; }
        else { previous = new PrepareTMXEntry(); previous.source = src; previous.translation = tra; previous.creationDate = System.currentTimeMillis(); }
        changesByDate.addLast(previous);
    }
    
    /** Register alternative translation. Parameters are split because they are not serializable **/
    public void registerAltTranslation(String src, String tra, String file, String id, String path, String prev, String next) {
        PrepareTMXEntry previous = null;
        // Step 1. Remove similar translation, if any
        for (Iterator<PrepareTMXEntry> iter = changesByDate.iterator(); iter.hasNext(); ) {
            PrepareTMXEntry current = iter.next();
            if (current.source.equals(src) && (current.otherProperties != null)) 
                if (file.equals(current.getPropValue("file")) && id.equals(current.getPropValue("id")) && path.equals(current.getPropValue("path"))
                &&  prev.equals(current.getPropValue("prev")) && next.equals(current.getPropValue("next"))) { 
                    iter.remove();
                    if (previous == null) previous = current;
                }
        }
        // Step 2. Add 
        if (previous != null) { previous.changeDate = System.currentTimeMillis(); previous.translation = tra; }
        else { 
            previous = new PrepareTMXEntry(); previous.source = src; previous.translation = tra; previous.creationDate = System.currentTimeMillis(); 
            previous.otherProperties = new ArrayList<>(3);
            if (file != null) previous.otherProperties.add(new TMXProp("file", file));
            if (id != null) previous.otherProperties.add(new TMXProp("id", id));
            if (path != null) previous.otherProperties.add(new TMXProp("path", path));
            if (prev != null) previous.otherProperties.add(new TMXProp("prev", prev));
            if (next != null) previous.otherProperties.add(new TMXProp("next", next));
        }
        changesByDate.addLast(previous);
    }    
    
    public long timeStamp() {
        return System.currentTimeMillis();
    }
}
