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

 Copyright (C) 2021 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;

import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;

import org.omegat.core.data.EntryKey;
import org.omegat.core.data.ITMXEntry;
import org.omegat.core.data.ProjectTMX;
import org.omegat.core.data.PrepareTMXEntry;
import org.omegat.util.TMXProp;

/**
 * Common interface to browse very large sets of entries
 * Like in JDBC's ResultSet, data may not be in memory
 * and contrarily to ITranslationEntry/ITMXEntry, retrieval may throw exception
 * 
 * @author Thomas Cordonnier
 */
public interface IEntryCursor {

    /** Go to next entry. Must be called before the first access **/
    public boolean next() throws Exception;
    
    // Next methods retreive entry content 
    // For simplification, we do not make distinction between internal/external TMX and glossary entries
    
    public String getEntrySource() throws Exception;
    
    public String getEntryTranslation() throws Exception;
    
    public String getEntryAuthor() throws Exception;
    
    public String getEntryLastModifier() throws Exception;
    
    public long getEntryCreationDate() throws Exception;
    
    public long getEntryLastModificationDate() throws Exception;
    
    public String getEntryNote() throws Exception;
    
    // Iterator through properties
    
    public boolean nextProperty() throws Exception;
    
    public String getCurrentPropertyName() throws Exception;
    
    public String getCurrentPropertyValue() throws Exception;
    
    public default PrepareTMXEntry toPrepareTMXEntry() throws Exception {
        PrepareTMXEntry tmx = new PrepareTMXEntry();
        // these fields are mandatory, so exception should be propagated
        tmx.source = getEntrySource(); tmx.translation = getEntryTranslation();
        try {
            // these fields are optional, they should not prevent return
            tmx.note = getEntryNote();
            tmx.creator = getEntryAuthor(); tmx.creationDate = getEntryCreationDate();
            tmx.changer = getEntryLastModifier(); tmx.changeDate = getEntryLastModificationDate();
            while (nextProperty()) {
               if (tmx.otherProperties == null) tmx.otherProperties = new java.util.ArrayList<>();
               tmx.otherProperties.add(new TMXProp(getCurrentPropertyName(), getCurrentPropertyValue()));
            }
        } catch (Exception ex) {
            
        }
        return tmx;
    }
    
    /** Try to build an entry key from properties. Warning: this method does consume the properties iteration! **/
    public default EntryKey buildEntryKey() throws Exception {
        boolean hasKey = false; Map<String,String> props = new HashMap<>();
        while (nextProperty()) {
            String key = getCurrentPropertyName();
            hasKey = key.equals(ProjectTMX.PROP_FILE) || key.equals(ProjectTMX.PROP_ID)
                || key.equals(ProjectTMX.PROP_PREV) || key.equals(ProjectTMX.PROP_NEXT) || key.equals(ProjectTMX.PROP_PATH);
            if (hasKey) props.put(key, getCurrentPropertyValue());
        }
        return hasKey ? new EntryKey (
            props.get(ProjectTMX.PROP_FILE),
            getEntrySource(),
            props.get(ProjectTMX.PROP_ID),
            props.get(ProjectTMX.PROP_PREV),
            props.get(ProjectTMX.PROP_NEXT),
            props.get(ProjectTMX.PROP_PATH)) : null;
    }
    
    // Implementations for already existing lists
    
    public static class TMXEntriesCursor implements IEntryCursor {
        private Iterator<? extends ITMXEntry> iter;
        private ITMXEntry current = null;
        
        public TMXEntriesCursor(Iterable<? extends ITMXEntry> entries) { iter = entries.iterator(); }
        public TMXEntriesCursor(Iterator<? extends ITMXEntry> entries) { iter = entries; }
        
        public boolean next() { 
            if (iter.hasNext()) { current = iter.next(); return true; } 
            else return false; 
        }
        
        public String getEntrySource() { return current.getSourceText(); } 
        public String getEntryTranslation() { return current.getTranslationText(); }
        public String getEntryAuthor() { return current.getCreator(); }
        public String getEntryLastModifier() { return current.getChanger(); }
        public long getEntryCreationDate() { return current.getCreationDate(); }   
        public long getEntryLastModificationDate() { return current.getChangeDate(); }
        public String getEntryNote() { return current.getNote(); }
        
        // Iterator through properties
        
        private Iterator<TMXProp> propIter = null;
        private TMXProp currentProp = null;
        
        public boolean nextProperty() {
            if (current.getProperties() == null) return false;
            if (propIter == null) propIter = current.getProperties().iterator();
            
            if (propIter.hasNext()) { currentProp = propIter.next();return true; }
            else { propIter = null; return false; }
        }
        
        public String getCurrentPropertyName() { return currentProp.getType(); }
        public String getCurrentPropertyValue() { return currentProp.getValue(); }
        
        public PrepareTMXEntry toPrepareTMXEntry() throws Exception {  // default works but this one is faster
            try { return (PrepareTMXEntry) current; }
            catch (ClassCastException cce) { return IEntryCursor.super.toPrepareTMXEntry(); }
        }
    }
}
