/*******************************************************************************
 * Copyright (c) 2002, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl;
                          
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.wst.xml.core.internal.Logger;
import org.eclipse.wst.xml.core.internal.XMLCoreMessages;
import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
import org.eclipse.wst.xml.core.internal.contentmodel.ContentModelManager;
import org.eclipse.wst.xml.core.internal.contentmodel.internal.annotation.AnnotationUtility;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManager;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManagerListener;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentReferenceProvider;
import org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl.GlobalCMDocumentCache.GlobalCacheQueryResponse;
import org.eclipse.wst.xml.core.internal.contentmodel.util.CMDocumentCache;
import org.eclipse.wst.xml.core.internal.preferences.XMLCorePreferenceNames;

import com.ibm.icu.text.MessageFormat;

/**
 *
 */
public class CMDocumentManagerImpl implements CMDocumentManager
{
  protected CMDocumentCache cmDocumentCache;
  protected CMDocumentReferenceProvider cmDocumentReferenceProvider;
  protected List listenerList = new Vector(); 
  protected Hashtable propertyTable = new Hashtable();
  protected Hashtable publicIdTable = new Hashtable();
  private boolean globalCMDocumentCacheEnabled ;

       
  public CMDocumentManagerImpl(CMDocumentCache cmDocumentCache, CMDocumentReferenceProvider cmDocumentReferenceProvider)
  {
    this.cmDocumentCache = cmDocumentCache;                                                                            
    this.cmDocumentReferenceProvider = cmDocumentReferenceProvider;
    setPropertyEnabled(PROPERTY_AUTO_LOAD, true);
    setPropertyEnabled(PROPERTY_USE_CACHED_RESOLVED_URI, false);
    setPropertyEnabled(PROPERTY_PERFORM_URI_RESOLUTION, true);
    initializeGlobalCMDocumentCacheSettings();
  }         

       
  public CMDocumentCache getCMDocumentCache()
  {
    return cmDocumentCache;
  }

 
  public void setPropertyEnabled(String propertyName, boolean value)
  {
    propertyTable.put(propertyName, value ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
    for (Iterator i = listenerList.iterator(); i.hasNext(); )
    {
      CMDocumentManagerListener listener = (CMDocumentManagerListener)i.next();
      listener.propertyChanged(this, propertyName);
    }
  }                                        

        
  public boolean getPropertyEnabled(String propertyName)
  {
    Object object = propertyTable.get(propertyName);
    return object != null && object.equals("true"); //$NON-NLS-1$
  }


  public void addListener(CMDocumentManagerListener listener)
  {
    listenerList.add(listener);
    cmDocumentCache.addListener(listener);
  }


  public void removeListener(CMDocumentManagerListener listener)
  {
    listenerList.remove(listener);
    cmDocumentCache.removeListener(listener);
  }                       
   
                   
  protected String lookupResolvedURI(String publicId)
  {
    String key = publicId != null ? publicId : ""; //$NON-NLS-1$
    return (String)publicIdTable.get(key);
  }
    

  protected String lookupOrCreateResolvedURI(String publicId, String systemId)
  {                    
    String resolvedURI = null;                  

    String key = publicId != null ? publicId : ""; //$NON-NLS-1$

    if (getPropertyEnabled(PROPERTY_USE_CACHED_RESOLVED_URI))
    {
      resolvedURI = (String)publicIdTable.get(key);
    }   

    if (resolvedURI == null)
    {
      resolvedURI = cmDocumentReferenceProvider.resolveGrammarURI(publicId, systemId);
      if (resolvedURI == null)
      {
        resolvedURI = ""; //$NON-NLS-1$
      }
      publicIdTable.put(key, resolvedURI);     
    }                       
  
    return resolvedURI;
  }


  public int getCMDocumentStatus(String publicId)
  {                                           
    int status = CMDocumentCache.STATUS_NOT_LOADED; 
    String resolvedURI = lookupResolvedURI(publicId);
    if (resolvedURI != null)
    {                      
      status = cmDocumentCache.getStatus(resolvedURI);
    }
    return status;
  }    
              

  public CMDocument getCMDocument(String publicId)
  {                                           
    CMDocument result = null;
    String resolvedURI = lookupResolvedURI(publicId);
    if (resolvedURI != null)
    {                      
      result = cmDocumentCache.getCMDocument(resolvedURI);
    }
    return result;
  }    
  

  /* (non-Javadoc)
 * @see org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManager#getCMDocument(java.lang.String, java.lang.String, java.lang.String)
 */
public CMDocument getCMDocument(String publicId, String systemId, String type)
  {                
    CMDocument cmDocument = null;                           
    String resolvedURI = null;

    if (getPropertyEnabled(PROPERTY_AUTO_LOAD))
    {
      // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=136399
      
      if (getPropertyEnabled(PROPERTY_PERFORM_URI_RESOLUTION))
      {
        resolvedURI = lookupOrCreateResolvedURI(publicId, systemId);
      }
      else
      {
        resolvedURI = systemId;
      }
    }    
    else
    {
      resolvedURI = lookupResolvedURI(publicId);
    } 

    if (resolvedURI != null)
    {                   
      int status = cmDocumentCache.getStatus(resolvedURI);
      if (status == CMDocumentCache.STATUS_LOADED)
      {                      
        cmDocument = cmDocumentCache.getCMDocument(resolvedURI);
      }   
      else if (status == CMDocumentCache.STATUS_NOT_LOADED)
      {     
        if (getPropertyEnabled(PROPERTY_AUTO_LOAD))
        {
          cmDocument = loadCMDocument(publicId, resolvedURI, type, getPropertyEnabled(PROPERTY_ASYNC_LOAD));
        }
      }
    }
    return cmDocument;   
  } 
  
  public void addCMDocumentReference(String publicId, String systemId, String type)
  {
    String resolvedURI = lookupOrCreateResolvedURI(publicId, systemId);
    if (resolvedURI != null && resolvedURI.length() > 0)
    {                                                                      
      int status = cmDocumentCache.getStatus(resolvedURI);
      if (status == CMDocumentCache.STATUS_NOT_LOADED)
      {                      
        loadCMDocument(publicId, resolvedURI, type, getPropertyEnabled(PROPERTY_ASYNC_LOAD));
      }         
    } 
  }
     

  public void addCMDocument(String publicId, String systemId, String resolvedURI, String type, CMDocument cmDocument)
  {
    String key = publicId != null ? publicId : ""; //$NON-NLS-1$
    publicIdTable.put(key, resolvedURI);
    cmDocumentCache.putCMDocument(resolvedURI, cmDocument);
  }


  protected CMDocument loadCMDocument(final String publicId, final String resolvedURI, final String type, boolean async)
  {                                                      
    CMDocument result = null;
                         
    //System.out.println("about to build CMDocument(" + publicId + ", " + unresolvedURI + " = " + resolvedURI + ")");
    if (async)
    {     
      cmDocumentCache.setStatus(resolvedURI, CMDocumentCache.STATUS_LOADING);
      //Thread thread = new Thread(new AsyncBuildOperation(publicId, resolvedURI, type));
      //thread.start();
      Job job = new Job(XMLCoreMessages.loading + resolvedURI)
      {
        public boolean belongsTo(Object family)
        {
          boolean result = (family == CMDocumentManager.class);
          return result;
        }
        
        protected IStatus run(IProgressMonitor monitor)
        {
          try
          {
        	  buildCMDocument(publicId, resolvedURI, type);
          }
          catch (Exception e)
          {
        	  Logger.logException(MessageFormat.format(XMLCoreMessages.CMDocument_load_exception, new Object[]{resolvedURI, publicId}), e);
          }
          return Status.OK_STATUS;
        }
      };
      job.schedule();
    }
    else
    {                
      result = buildCMDocument(publicId, resolvedURI, type);
    }          
    return result;
  } 

    

  public synchronized CMDocument buildCMDocument(String publicId, String resolvedURI, String type)
  {                                     
    cmDocumentCache.setStatus(resolvedURI, CMDocumentCache.STATUS_LOADING);
    boolean documentCacheable = false;
    if(globalCMDocumentCacheEnabled) {
    	GlobalCacheQueryResponse response = GlobalCMDocumentCache.getInstance().getCMDocument(resolvedURI);
    	CMDocument cachedCMDocument = response.getCachedCMDocument();
    	documentCacheable = response.isDocumentCacheable();
    	if(cachedCMDocument != null) {
    		cmDocumentCache.putCMDocument(resolvedURI, cachedCMDocument);
    		return cachedCMDocument;
    	}
    }

    CMDocument result = null;         
    if (resolvedURI != null && resolvedURI.length() > 0)
    {
      // TODO... pass the TYPE thru to the CMDocumentBuilder
      result = ContentModelManager.getInstance().createCMDocument(resolvedURI, type);
    }
    if (result != null)
    { 
      // load the annotation files for the document 
      if (publicId != null)
      {    
        AnnotationUtility.loadAnnotationsForGrammar(publicId, result);
      }
      if(globalCMDocumentCacheEnabled && documentCacheable) {
    	  GlobalCMDocumentCache.getInstance().putCMDocument(resolvedURI, result);
      }
      cmDocumentCache.putCMDocument(resolvedURI, result);
    }
    else
    {
      cmDocumentCache.setStatus(resolvedURI, CMDocumentCache.STATUS_ERROR);
    }
    return result;
  } 

  public void removeAllReferences()
  {
    // TODO... initiate a timed release of the entries in the CMDocumentCache
    publicIdTable = new Hashtable();
  }
  
  private void initializeGlobalCMDocumentCacheSettings() {
	  Preferences preferences = XMLCorePlugin.getDefault().getPluginPreferences();
	  if(preferences != null) {
		  globalCMDocumentCacheEnabled = preferences.getBoolean(XMLCorePreferenceNames.CMDOCUMENT_GLOBAL_CACHE_ENABLED);
	  }
  }

}                                            
