/* 9P Protocol JAVA
 * PFS/pfile.java
 * Autor: Jaime Garzon (2005) jgarzon@gmail.com
 *
 * Descripcion: Fichero pfile
 * Esta clase es una implementacion abstracta de un fichero
 * , la utilidad de esta clase es inducir a los subtipos
 * un interface de metodos comun.
*/

package PFS;

import java.io.*;
import java.util.*;
import P9P.type.*;

public abstract class pfile {

   protected Exception pfileDeleted;
   protected boolean isDeleted=false;
   protected Vector cache=new Vector();
   protected pfile pfilefather;

   //*********************
   // Metodos abstractos
   //*********************

   public abstract boolean isDir() throws Exception;
   public abstract boolean isReadable();
   public abstract boolean isWritable();
   public abstract boolean isExecutable();
   public abstract String getname();
   public abstract stat9P getStat();

   public abstract void wstat(stat9P newst) throws Exception;
   public abstract pfile create(String name, long perm) throws Exception;
   public abstract pfile walk(String s) throws Exception;
   public abstract void remove() throws Exception;

   //Metodos protected, para que accedan a traves de un pdescriptor.
   //   puesto que es necesario que esten abiertos.
   protected abstract void open(short nmode)  throws Exception;
   protected abstract byte[] read(long offset, long count) throws Exception;
   protected abstract int write(long offset, byte[] data) throws Exception;
   protected abstract void truncate(int size) throws Exception;
   protected abstract boolean canGetName(String s);
   protected abstract void close() throws Exception;
   protected abstract String[] list ();
   protected abstract boolean fileExists();

   //**********************
   // Metodos
   //**********************

   public qid9P getqid() {
      return (getStat()).qid;
   }

   protected boolean isOpenExclusiveMode(long perm) {
      return (((int)perm & cons.DMEXCL)==cons.DMEXCL);
   }

   //creacion arbol dinamico pfile
   protected void addPfileCache(pfile p) {
      cache.addElement(p);
   }

   protected pfile getPfileCache(String name) {
      int tam;
      pfile ptmp;
      String cadtmp;

      if (name.equals("."))
         return this;
      if (name.equals(".."))
         return pfilefather;
      if (cache.isEmpty())
         return null;

      tam=cache.size();
      for(int i=0; i<tam; i++) {
         ptmp = (pfile) cache.elementAt(i);
         cadtmp = ptmp.getname();
         if ((cadtmp==null)|(!ptmp.fileExists())) {
             cache.removeElementAt(i);
             i--;
             tam--;
         }
         else if (name.equals(cadtmp)) return ptmp;
      }
      return null;
   }

   public pfile[] getListOfNodes() {
      String [] nom;
      pfile[] tabla;
      nom=list();

      if (nom==null) return null;

      int tam=nom.length;
      tabla = new pfile[tam];
      for(int i=0; i<tam; i++)
               try {
               tabla[i] = walk(nom[i]);
               } catch (Exception e) {}
      return tabla;
   }

   //verifica modo de apertura
   protected void checkOpenMode(short m) throws Exception {

      if (checkFlag(m,cons.OREAD)&&!isReadable())
         throw new Exception("Error no read mode");

      if (checkFlag(m,cons.OWRITE)&&!isWritable())
         throw new Exception("Error no write mode");

      if (checkFlag(m,cons.ORDWR)&&!(isReadable()&isWritable()))
         throw new Exception("Error no rdwr mode");

      if (checkFlag(m,cons.OEXEC)&&!isExecutable())
         throw new Exception("Error no execute mode");

      if (checkFlag(m,cons.OTRUNC)&&!isWritable())
         throw new Exception("Error no write mode");
   }

   protected stat9P checkWStat(stat9P newst) throws Exception {

      stat9P stat=getStat();

      //DATOS INVARIANTES

      newst.qid.type.value = (short)(newst.mode.value / (2^24));

      newst.qid.path.value = stat.qid.path.value;
      newst.qid.vers.value = stat.qid.vers.value;
      newst.qid.type.value = stat.qid.type.value;

      newst.length.value = stat.length.value;
      newst.dev.value = stat.dev.value;
      newst.type.value = stat.type.value;

      newst.atime.value = stat.atime.value;
      newst.mtime.value=stat.mtime.value;

      newst.uid.value = stat.uid.value;
      newst.muid.value = stat.muid.value;
      newst.gid.value=stat.gid.value;

      //CAMBIO DE MODO
      if (newst.mode.value==0xFFFFFFFFL)
         newst.mode.value=stat.mode.value;
      else if (isDirectory(newst.mode.value)!=isDirectory(stat.mode.value))
            throw new Exception("Error wstat mode change");

      //CAMBIO DE NOMBRE (si Writemode & existe nombre)
      if (newst.name.value=="")
         newst.name.value=stat.name.value;
      else if ((stat.name.value!=newst.name.value)
           &&isWritable()&&(canGetName(newst.name.value)))
            throw new Exception("Error wstat name change");

      return stat;
   }

   //metodos internos
   protected static int longToInt (long l) {
      int i=(int) l;
      if ((l<=(long) Integer.MAX_VALUE)&&(l>=0))
         return i;
      else
         return -1;
   }

   private boolean checkFlag(short m, byte b) {
      return (m & b)==b;
   }

   protected void switchToDeletedMode() {
      pfileDeleted = new Exception("File maybe deleted, not exist.");
      isDeleted=true;
      pfilefather=null;
      cache=null;
   }

   protected boolean isDirectory(long perm) {
      return (((int)perm & cons.DMDIR)==cons.DMDIR);
   }
}