/* 9P Protocol JAVA
 * PFS/pfiledisk.java
 * Autor: Jaime Garzon (2005) jgarzon@gmail.com
 *
 * Descripcion: Fichero pfile en disco
*/

package PFS;

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

public class pfiledisk extends pfile {

   private File f;
   private RandomAccessFile acc;

   //***** CONSTRUCTORS *******

   public pfiledisk(String z) {
      f=new File(z);
      pfilefather=null;
   }

   protected pfiledisk(File file, pfile p) {
      f=file;
      pfilefather=p;
   }

   //**** METHODS (INFO) ******

   public boolean isDir() throws Exception {
      if (isDeleted) throw pfileDeleted;
      return f.isDirectory();
   }

   public boolean isReadable() {
      return f.canRead();
   }

   public boolean isWritable() {
      return f.canWrite();
   }

   public boolean isExecutable() {
      return isReadable();
   }

   protected boolean fileExists () {
         return f.exists();
   }

   public String getname() {
      if (isDeleted)
         return null;
      return f.getName();
   }

   public stat9P getStat() {
      return getStat(f);
   }

   protected stat9P getStat(File file) {

      if (isDeleted)
         return null;
      stat9P st= new stat9P();

      //st.mode.value = getMode(file);
      st.type.value = 0;
      st.dev.value = 0;
      st.length.value = file.length();

      st.qid.vers.value = 0;
      st.qid.path.value = (long) file.hashCode();
      if (file.isDirectory()) {
          st.mode.value=0x80000007L;
          st.qid.type.value=0x80;
      }
      else {
          st.mode.value=0x00000007L;
          st.qid.type.value=0x00;
      }

      st.name.value = file.getName();
      st.muid.value = "nobody";
      st.uid.value = "nobody";
      st.gid.value = "nogroup";

      st.mtime.value = f.lastModified()/1000;
      st.atime.value = st.mtime.value ;

      return st;
   }

   protected boolean canGetName(String s) {
      File aux;
      if (isDeleted)
         return false;
      aux = f.getParentFile();
      if (aux==null)
         return true;

      aux=new File(aux.getAbsolutePath() + File.separatorChar + s);
      return aux.exists();
   }

   //**** METHODS (BUILD) ******

   public pfile walk(String s) throws Exception {
      File ftmp;
      pfile ptmp;
      if (isDeleted)
         throw pfileDeleted;
      if (!isExecutable())
         throw new Exception("Access permission denied");
      ptmp=getPfileCache(s);

      if (ptmp==null)   {
         ftmp=new File(f.getAbsolutePath() + File.separatorChar + s);
         if (!ftmp.exists())
            throw new Exception("File not found");
         ptmp=new pfiledisk(ftmp, this);
      }
      return ptmp;
   }

   protected String[] list () {
      return f.list();
   }

   protected synchronized void open(short nmode) throws Exception {
      if (isDeleted)
         throw pfileDeleted;
      checkOpenMode(nmode);

      String cad="";
      if ((nmode & cons.OREAD)==cons.OREAD)
         cad="r";
      if ((nmode & cons.OWRITE)==cons.OWRITE)
         cad="rw";
      if (!isDir())
         acc= new RandomAccessFile(f, cad);
   }

   protected void close() throws Exception {
      acc.close();
   }

   public synchronized pfile create (String name, long perm) throws Exception {

      long m;
      stat9P stat=getStat();
      pfile ptmp;
      File aux;

      if (!isDir())
         throw new Exception("Cant create, not directory");
      aux=new File(f.getAbsolutePath() + File.separatorChar + name);

      if (isDirectory(perm))
         aux.mkdir();
      else
         aux.createNewFile();

      ptmp= new pfiledisk(aux, this);
      addPfileCache(ptmp);
      return ptmp;
   }

   protected synchronized byte[] read(long ofset, long coun) throws Exception {

      byte[] b;
      int byteread;
      int l=longToInt(ofset);
      int c=longToInt(coun);

      if (isDeleted)
         throw pfileDeleted;
      if (l<0)
         throw new Exception("Bad offset");
      if (c<0)
         throw new Exception("Bad count");

      if(isDir()) {
         int tam;
         int sum=0;
         boolean escritura=false;
         ByteArrayOutputStream aux=new ByteArrayOutputStream();
         File[] lst=f.listFiles();
         stat9P st;
         if (lst!=null) {
            for(int i=0;i<lst.length;i++) {
               if (l==sum)
                  escritura=true;
               st=getStat(lst[i]);
               tam=st.len();
               sum=sum+tam;
               if (escritura) {
                  if((aux.size()+tam)<=c) st.write(aux);
                  else break;
               }
            }
            b=aux.toByteArray();
         }
         else throw new Exception("File not read mode");
      }
      else {
         b=new byte[c];
         try {
            acc.seek(l);
            byteread = acc.read(b);
         }
         catch( Exception e ) {
            throw e;
         }
         if (byteread<0)
            byteread=0;
         if (byteread<c) {
            byte[] new_b = new byte[byteread];
            for (int j=0; j<byteread; j++)
               new_b[j]=b[j];
            b=new_b;
         }
      }
      return b;
   }

   protected synchronized int write(long ofset, byte[] d) throws Exception {

      int l=longToInt(ofset);
      if (isDeleted)
         throw pfileDeleted;
      if (l<0)
         throw new Exception("Bad offset");

      acc.seek(l);
      acc.write(d);
      return d.length;
   }

   protected void truncate(int size) throws Exception {
      if (isDeleted)
         throw pfileDeleted;
      acc.setLength((long) size);
   }

   public synchronized void remove() throws Exception {
      if (isDeleted)
         throw pfileDeleted;
      if (!f.delete())
         throw new Exception("Cant delete");
      //acc.close();
      f=null;
      switchToDeletedMode();
   }

   public synchronized void wstat(stat9P newst) throws Exception {
      stat9P stat=checkWStat(newst);
      if (isDeleted)
         throw pfileDeleted;
      stat=newst;
      setName(stat.name.value);
      //n.setMode(stat.mode.value);
   }

   //Metodos Privados

   private long getMode(File file) {
      long flag;

      //Escritura
      if (file.isDirectory())
         flag=0x80000000L;
      else
         flag=0x0L;

      //Lectura y Ejecucion
      if (file.canRead())
         flag=flag|0005;
      if (file.canWrite())
         flag=flag|0002;

      return flag;
   }

   private void setName(String cad) throws Exception {
      File newName = new File(cad);
      if (!f.renameTo(newName))
         throw new Exception("Cant rename or move file");
   }
}