package com.limegroup.gnutella.messages.vendor;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.limewire.io.BadGGEPBlockException;
import org.limewire.io.BadGGEPPropertyException;
import org.limewire.io.GGEP;
import org.limewire.service.ErrorService;

import com.limegroup.gnutella.messages.BadPacketException;

public final class UpdateResponse extends AbstractVendorMessage implements VendorMessage.ControlMessage {
    
    private static final int NON_GGEP_VERSION = 1;
    private static final int OLD_KEY_VERSION = 2;
    private static final int NEW_KEY_VERSION = 3;
    public static final int VERSION = 3;

    private byte [] update;
    
    /**
     * Constructs a new UpdateResponse message from the network.
     * @param network TODO
     */
    UpdateResponse(byte[] guid, byte ttl, byte hops, int version, byte[] payload, Network network) 
                                                     throws BadPacketException {
        super(guid, ttl, hops, F_LIME_VENDOR_ID, F_UPDATE_RESP, version, payload, network);
        
        if (version == NON_GGEP_VERSION)
            update = payload;
        else {
            // try to parse a GGEP block
            try {
                GGEP ggep = new GGEP(payload,0,null);
                if (ggep.hasKey(UpdateRequest.UNCOMPRESSED_UPDATE_KEY))
                    update = ggep.getBytes(UpdateRequest.UNCOMPRESSED_UPDATE_KEY);
                else if (ggep.hasKey(UpdateRequest.COMPRESSED_UPDATE_KEY))
                    update = ggep.getBytes(UpdateRequest.COMPRESSED_UPDATE_KEY);
                else throw new BadPacketException("no update in GGEP?");
            } catch (BadGGEPPropertyException bad) {
                throw new BadPacketException("bad ggep property");
            } catch (BadGGEPBlockException notSoBad) {
                update = payload;
            }
        }
    }
    
    /**
     * Constructs an outgoing message with the payload being the signed parameter body.
     */
    private UpdateResponse(byte[] body, int version) {
        super(F_LIME_VENDOR_ID, F_UPDATE_RESP, version, body);
    }

    public static UpdateResponse createUpdateResponse(byte [] update, UpdateRequest request) {
        if(!request.isOldRequest()) {
            // If it's not an old request, just always toss it in a GGEP.
            byte[] ggep = createGGEP(request, update);
            return new UpdateResponse(ggep, VERSION);
        } else if (!request.hasGGEP()) {
            // If it's an old request w/o ggep, don't put in GGEP
            return new UpdateResponse(update, NON_GGEP_VERSION);
        } else {
            // Old request w/ ggep, toss in the old GGEP.
            byte[] ggep = createGGEP(request, update);
            return new UpdateResponse(ggep, OLD_KEY_VERSION);
        }
    }
    
    public boolean isNewVersion() {
        return getVersion() >= NEW_KEY_VERSION;
    }
    
    private static byte[] createGGEP(UpdateRequest request, byte[] update) {
        GGEP ggep = new GGEP();
        if (request.requestsCompressed()) {
            ggep.putCompressed(UpdateRequest.COMPRESSED_UPDATE_KEY, update);
        } else {
            ggep.put(UpdateRequest.UNCOMPRESSED_UPDATE_KEY, update);
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ggep.write(baos);
        } catch (IOException bad) {
            ErrorService.error(bad);
        }
        
        return baos.toByteArray();
    }
    
    public byte[] getUpdate() {
        return update;
    }
}
