package org.wikiwebserver.sync;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.wikiwebserver.core.SecurityMan;
import org.wikiwebserver.core.WareHouse;
import org.wikiwebserver.handler.http.FormData;
import org.wikiwebserver.handler.http.HTTPException;
import org.wikiwebserver.handler.http.HTTPHandler;
import org.wikiwebserver.handler.http.HTTPPostReader;
import org.wikiwebserver.handler.http.interfaces.HTTPResponder;

import org.wikiwebserver.sync.HTTPDataLocationConnector.Command;

import page.tools.entity.User;

public class HTTPDataLocationResponder extends LocalFileDataLocation 
    implements DataLocation, HTTPResponder {
    

    public Object respond(HTTPHandler conn) throws IOException {
        System.out.println(conn.getRequest());
        FormData formData = conn.getRequest().getFormData();
        if (formData == null) {
            throw new HTTPException(500, "No form data found");            
        }
        
        Command command = null;
        String commandString = formData.getFirst("command");            
        try {
            command = Command.valueOf(commandString.toUpperCase());
        } catch (Exception ex) {
            throw new HTTPException(500, "Command not implemented");
        }
        
        
        // Configure LocalFileDataLocation
        String basePath = formData.getFirst("basePath");   
        if (basePath == null || basePath.length() == 0) basePath = "./";
        
        String superPassword = conn.getRequest().getHeaders().getFirst("SuperPassword");
        if (superPassword != null && !superPassword.equals("null")) {
            // Will throw security exception if incorrect
            SecurityMan.checkSuperPassword(superPassword);
            
            List<File> excludedFiles = new ArrayList<File>();
            excludedFiles.add(new File("./gallery"));
            excludedFiles.add(new File("./example_node"));
            excludedFiles.add(new File("./user"));
            setExcludedFiles(excludedFiles);
            
        }
        else {
            String userID = conn.getRequest().getHeaders().getFirst("UserID");
            if (userID == null || userID.length() == 0) {
                throw new HTTPException(403, "User identification not specified");
            }
            
            User user = User.getUserById(userID);
            if (user == null) {
                throw new HTTPException(403, "User not found");
            }
            
            String sessionAuth = conn.getRequest().getHeaders().getFirst("SessionAuth");
            // Will throw security exception if incorrect
            user.checkAuthorised(sessionAuth);
    
            // Restrict modifications to user area
            if (!basePath.startsWith("user/u" + user.getId())) {
                throw new HTTPException(403, "Only user directory can be synchronized");
            } 
            
            // This is a fix so that accurate data is stored about this user transfer
            conn.getRequest().getHeaders().setResponseCookie("userID", userID);
        }
        
        setBasePath(new File(basePath));        
        
        // Configure referenced FileItem
        String relPath = formData.getFirst("relPath");            
        long length = 0;
        long lastModified = 0;            
        if (relPath != null) {
            lastModified = Long.parseLong(formData.getFirst("lastModified"));                
            length = Long.parseLong(formData.getFirst("length"));
        }
        
        // Configure other data
        long remoteTime = Long.parseLong(formData.getFirst("time"));    
        String syncId = formData.getFirst("syncID"); 
        // Bug fix
        if (syncId != null && syncId.equals("null")) syncId = null;
        
        try {
        
            switch (command) {
                case TIME_OFFSET :
                    return System.currentTimeMillis() - remoteTime;
                case DIFF :
                    FileItemBatch batch = getBatch(syncId);
                    batch.writeBatch(conn.getOutputStream());
                    break;
                case INPUTSTREAM :
                    InputStream in =  getInputStream(relPath);
                    try {
                        proxyStream(in, conn.getOutputStream());
                    } finally { in.close(); }
                    break;
                case TRANSFER_DATA :
                    transferData((HTTPPostReader)conn.getRequest().getData(), length, relPath);
                    break;
                case COMMIT_TRANSFER :
                    commitTransfer(relPath, lastModified);
                    break;
                case MKDIR :
                    mkdir(relPath, lastModified);
                    break;
                case DELETE :
                    delete(relPath);
                    break;
                case SAVE_SYNC_DATA:
                    if (syncId != null) saveBatch(syncId);
                    WareHouse.notifyFileChange();
                    break;
            }
        }
        catch (InterruptedException ex) {
            throw new HTTPException(500, "Synchronization Interrupted", ex);
        }
        catch (Exception ex) {
            throw new HTTPException(500, ex.getMessage(), ex);
        }            
                
        // Done
        return null;
    }

    private static void proxyStream(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[32 * 1024];
        int r = in.read(buffer);
        while (r > 0) {
            out.write(buffer, 0, r);
            r = in.read(buffer);
        }
    }       
}

