/*
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xcmis.search.lucene.index;

import org.xcmis.spi.utils.Logger;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by The eXo Platform SAS.
 * 
 * @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a>
 * @version $Id: IndexInfo.java 2 2010-02-04 17:21:49Z andrew00x $
 */
public class IndexInfo
{
   /**
    * Class logger.
    */
   private final Logger log = Logger.getLogger(IndexInfo.class);

   /**
    * For new segment names.
    */
   private int counter = 0;

   /**
    * Flag that indicates if index infos needs to be written to disk.
    */
   private boolean dirty = false;

   /**
    * List of index names
    */
   private List<String> indexes = new ArrayList<String>();

   /**
    * Set of names for quick lookup.
    */
   private Set<String> names = new HashSet<String>();

   /**
    * Name of the file where the infos are stored.
    */
   private final String name;

   /**
    * Creates a new IndexInfos using <code>fileName</code>.
    * 
    * @param fileName the name of the file where infos are stored.
    */
   public IndexInfo(String fileName)
   {
      this.name = fileName;
   }

   /**
    * Returns <code>true</code> if this index infos exists in <code>dir</code>.
    * 
    * @param dir the directory where to look for the index infos.
    * @return <code>true</code> if it exists; <code>false</code> otherwise.
    */
   public boolean exists(File dir)
   {
      return new File(dir, name).exists();
   }

   /**
    * Returns the name of the file where infos are stored.
    * 
    * @return the name of the file where infos are stored.
    */
   public String getFileName()
   {
      return name;
   }

   /**
    * Reads the index infos.
    * 
    * @param dir the directory from where to read the index infos.
    * @throws IOException if an error occurs.
    */
   public void read(File dir) throws IndexException
   {
      try
      {
         InputStream in = new FileInputStream(new File(dir, name));
         try
         {
            DataInputStream di = new DataInputStream(in);
            counter = di.readInt();
            for (int i = di.readInt(); i > 0; i--)
            {
               String indexName = di.readUTF();
               indexes.add(indexName);
               names.add(indexName);
            }
         }
         finally
         {
            in.close();
         }
      }
      catch (FileNotFoundException e)
      {
         throw new IndexException(e.getLocalizedMessage(), e);
      }
      catch (IOException e)
      {
         throw new IndexException(e.getLocalizedMessage(), e);
      }
   }

   /**
    * Writes the index infos to disk if they are dirty.
    * 
    * @param dir the directory where to write the index infos.
    * @throws IOException if an error occurs.
    */
   public void write(File dir) throws IOException
   {
      // do not write if not dirty
      if (!dirty)
      {
         return;
      }

      File nu = new File(dir, name + ".new");
      OutputStream out = new FileOutputStream(nu);
      try
      {
         DataOutputStream dataOut = new DataOutputStream(out);
         dataOut.writeInt(counter);
         dataOut.writeInt(indexes.size());
         for (int i = 0; i < indexes.size(); i++)
         {
            dataOut.writeUTF(getName(i));
         }
      }
      finally
      {
         out.close();
      }
      // delete old
      File old = new File(dir, name);
      if (old.exists() && !old.delete())
      {
         throw new IOException("Unable to delete file: " + old.getAbsolutePath());
      }
      if (!nu.renameTo(old))
      {
         throw new IOException("Unable to rename file: " + nu.getAbsolutePath());
      }
      dirty = false;
   }

   /**
    * Returns the index name at position <code>i</code>.
    * 
    * @param i the position.
    * @return the index name.
    */
   public String getName(int i)
   {
      return indexes.get(i);
   }

   /**
    * Returns the number of index names.
    * 
    * @return the number of index names.
    */
   public int size()
   {
      return indexes.size();
   }

   /**
    * Adds a name to the index infos.
    * 
    * @param name the name to add.
    */
   public void addName(String indexName)
   {
      if (names.contains(indexName))
      {
         throw new IllegalArgumentException("already contains: " + indexName);
      }
      indexes.add(indexName);
      names.add(indexName);
      dirty = true;
   }

   /**
    * Removes the name from the index infos.
    * 
    * @param name the name to remove.
    */
   public void removeName(String indexName)
   {
      indexes.remove(indexName);
      names.remove(indexName);
      dirty = true;
   }

   /**
    * Removes the name from the index infos.
    * 
    * @param i the position.
    */
   public void removeName(int i)
   {
      String indexName = indexes.remove(i);
      names.remove(indexName);
      dirty = true;
   }

   /**
    * Returns <code>true</code> if <code>name</code> exists in this
    * <code>IndexInfos</code>; <code>false</code> otherwise.
    * 
    * @param name the name to test existence.
    * @return <code>true</code> it is exists in this <code>IndexInfos</code>.
    */
   public boolean contains(String indexName)
   {
      return names.contains(indexName);
   }

   /**
    * Returns a new unique name for an index folder.
    * 
    * @return a new unique name for an index folder.
    */
   public String newName()
   {
      dirty = true;
      return "_" + Integer.toString(counter++, Character.MAX_RADIX);
   }
}
