package savegame;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;
import w.DoomIO;
import w.IReadableDoomObject;
import w.IWritableDoomObject;
import defines.skill_t;

import static data.Defines.VERSION;
import static data.Limits.MAXPLAYERS;
import static data.Limits.SAVESTRINGSIZE;
import static data.Limits.VERSIONSIZE;

/** The header of a vanilla savegame.
 *  
 *  It contains a fixed-length, null-terminated string of 24 bytes max, in any case.
 *  Then a 16-byte "version string", which normally reads "version 109".
 *  Then bytes that record:
 *  skill +1
 *  episode +1
 *  map +1
 *  players in game +4
 *  gametime +3 (as 24-bit big-endian)
 *  
 *  So the header has an total size of *drum roll* 50 bytes.
 *  
 * 
 * @author admin
 *
 */


public class  VanillaDSGHeader implements IDoomSaveGameHeader,IReadableDoomObject, IWritableDoomObject, CacheableDoomObject{
    
public String name; // max size SAVEGAMENAME
public String vcheck;
    // These are for DS
public skill_t gameskill;
public int gameepisode;
public int gamemap;
public boolean[] playeringame;
    /** what bullshit, stored as 24-bit integer?! */
 public int leveltime;
    // These help checking shit.
public boolean wrongversion;
   public boolean properend;
    
   public VanillaDSGHeader(){
       this.playeringame=new boolean[MAXPLAYERS];
   }
   
   
   @Override
   public void unpack(ByteBuffer buf)
           throws IOException {
       name=DoomBuffer.getNullTerminatedString(buf, SAVESTRINGSIZE);
       vcheck=DoomBuffer.getNullTerminatedString(buf, VERSIONSIZE);
       String vcheckb= ("version "+VERSION);
       // no more unpacking, and report it.
       if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return;
       gameskill = skill_t.values()[buf.get()]; 
       gameepisode = buf.get();
       gamemap = buf.get();
       
              for (int i=0 ; i<MAXPLAYERS ; i++)
       playeringame[i] = buf.get()!=0; 

       // load a base level (this doesn't advance the pointer?) 
       //G_InitNew (gameskill, gameepisode, gamemap); 
    
       // get the times 
       int a = C2JUtils.toUnsignedByte(buf.get()); 
       int b = C2JUtils.toUnsignedByte(buf.get());
       int c =  C2JUtils.toUnsignedByte(buf.get());
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       leveltime = (a<<16) | (b<<8) | c; 

       // Mark this position...
       buf.mark();
       buf.position(buf.limit()-1);
       if (buf.get() != 0x1d) properend=false; else
           properend=true;
       buf.reset();
           
       // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper
       // methods in the game engine itself.
   }
   
   @Override
   public void write(DataOutputStream f)
           throws IOException {
       DoomIO.writeString(f,name,SAVESTRINGSIZE);
       DoomIO.writeString(f,vcheck,VERSIONSIZE);
       f.writeByte(gameskill.ordinal()); 
       f.writeByte(gameepisode);
       f.writeByte(gamemap);
       for (int i=0 ; i<MAXPLAYERS ; i++) 
       f.writeBoolean(playeringame[i]); 

       // load a base level (this doesn't advance the pointer?) 
       //G_InitNew (gameskill, gameepisode, gamemap); 
    
       // get the times 
       byte a = (byte) (0x0000FF&(leveltime>>16)); 
       byte b = (byte) (0x00FF&(leveltime>>8));
       byte c =  (byte) (0x00FF&(leveltime));
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       f.writeByte(a);
       f.writeByte(b);
       f.writeByte(c);

       // The end. This is actually just the header, so we don't "end" here just yet.
       // f.writeByte(0x1d);
           
   } 
   
   @Override
   public void read(DataInputStream f)
           throws IOException {
       name= DoomIO.readNullTerminatedString(f,SAVESTRINGSIZE);
       vcheck=DoomIO.readNullTerminatedString(f,VERSIONSIZE);
       gameskill=skill_t.values()[f.readUnsignedByte()]; 
       gameepisode=f.readByte();
       gamemap=f.readByte();
       for (int i=0 ; i<MAXPLAYERS ; i++) 
           playeringame[i]=f.readBoolean(); 
    
       // get the times 
       int a = f.readUnsignedByte(); 
       int b = f.readUnsignedByte();
       int c = f.readUnsignedByte();
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       leveltime = (a<<16) | (b<<8) | c; 
           
   } 
   
   ////////////////////////// NASTY GETTERS //////////////////////////////
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String getVersion() {
        return vcheck;
    }
    @Override
    public void setVersion(String vcheck) {
        this.vcheck = vcheck;
    }
    @Override
    public skill_t getGameskill() {
        return gameskill;
    }
    @Override
    public void setGameskill(skill_t gameskill) {
        this.gameskill = gameskill;
    }
    @Override
    public int getGameepisode() {
        return gameepisode;
    }
    @Override
    public void setGameepisode(int gameepisode) {
        this.gameepisode = gameepisode;
    }
    @Override
    public int getGamemap() {
        return gamemap;
    }
    @Override
    public void setGamemap(int gamemap) {
        this.gamemap = gamemap;
    }
    @Override
    public boolean[] getPlayeringame() {
        return playeringame;
    }
    @Override
    public void setPlayeringame(boolean[] playeringame) {
        this.playeringame = playeringame;
    }
    @Override
    public int getLeveltime() {
        return leveltime;
    }
    @Override
    public void setLeveltime(int leveltime) {
        this.leveltime = leveltime;
    }
    @Override
    public boolean isWrongversion() {
        return wrongversion;
    }
    @Override
    public void setWrongversion(boolean wrongversion) {
        this.wrongversion = wrongversion;
    }
    @Override
    public boolean isProperend() {
        return properend;
    }

}