package jp.sourceforge.shovel.util;

import static jp.sourceforge.shovel.CommonConst.*;
import static org.seasar.framework.container.ContainerConstants.*;
import static org.apache.commons.httpclient.util.DateUtil.*;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.upload.FormFile;
import org.apache.commons.httpclient.util.DateUtil;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

import jp.sourceforge.shovel.entity.IClient;
import jp.sourceforge.shovel.entity.IMimeType;
import jp.sourceforge.shovel.entity.IServerFile;
import jp.sourceforge.shovel.exception.ApplicationException;
import jp.sourceforge.shovel.logic.IMimeTypeLogic;

public class ServerFileUtil {
    // HTTP_ACCEPTがない場合、行内データにするMIMEタイプ
    static List<String> allowFormatNames_;
    //ルートディレクトリ名
    static String root_;
    static {
        allowFormatNames_ = new ArrayList<String>();
        allowFormatNames_.add("jpeg");
        allowFormatNames_.add("gif");
        allowFormatNames_.add("png");
    };
    
    public static BufferedImage getBufferedImage(IServerFile serverFile) throws ApplicationException {
        try {
            ImageInputStream is = ImageIO.createImageInputStream(serverFile.getInputStream());
            return ImageIO.read(is);
            /*
            ImageInputStream is = ImageIO.createImageInputStream(serverFile.getInputStream());
            IMimeType mimeType = getMimeTypeLogic().getMimeTypeByFileName(serverFile.getName());
            String mime;
            if (mimeType == null) {
                mime = serverFile.getMime();
            } else {
                mime = mimeType.getMimeType();
            }

            Iterator<ImageReader> i = ImageIO.getImageReadersByMIMEType(mime);
            BufferedImage image = null;
            while (i.hasNext()) {
                ImageReader reader = i.next();
                reader.setInput(is, true);
                image = reader.read(reader.getMinIndex());
                if (image != null) {
                    String formatName = reader.getFormatName().toLowerCase();
                    if (allowFormatNames_.contains(formatName)) {
                        break;
                    }
                }
            }
            return image;
            */
        } catch (IOException cause) {
            //TODO
            ApplicationException e = new ApplicationException("");
            e.initCause(cause);
            throw e;
        }
    }
    static S2Container getContainer() {
        return (S2Container)SingletonS2ContainerFactory.getContainer();
    }
    static IMimeTypeLogic getMimeTypeLogic() {
        S2Container container = getContainer();
        return (IMimeTypeLogic)container.getComponent( IMimeTypeLogic.class );
    }
    static Properties getCommonProperties() {
        return (Properties)getContainer().getComponent(COMMON_PROPERTIES);
    }
    public static boolean isText( String mimeType ) {
        return mimeType.startsWith( "text/" );
    }
    public static void prepareDownload(IServerFile serverFile) throws ApplicationException {
        HttpServletRequest request = (HttpServletRequest)getContainer().getComponent(REQUEST_NAME);
        String userAgent = request.getHeader("User-Agent").toLowerCase();
        
        IClient client = HttpUtil.detectClient(userAgent);
        
        IMimeTypeLogic mimeTypeManager = getMimeTypeLogic();
        String fileName = serverFile.getName();
        IMimeType mimeType = mimeTypeManager.getMimeType(".jpeg");
        StringBuilder disposition = new StringBuilder("attachment");
        
        HttpServletResponse response = (HttpServletResponse)getContainer().getComponent(RESPONSE_NAME);
        disposition.append("; filename=\"");
        disposition.append(client.encodeText(fileName));
        disposition.append("\"");
        response.setHeader("Content-Disposition", disposition.toString());
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(serverFile.getCreatedTime());
        String time = DateUtil.formatDate(calendar.getTime(), PATTERN_RFC1123);
        response.setHeader("Last-Modified", time);
        calendar.set(1970, 1, 1, 0, 0, 0);
        time = DateUtil.formatDate(calendar.getTime(), PATTERN_RFC1123);
        response.setHeader("Expires", HttpUtil.getRFC1123Time(calendar.getTime()));
        response.setContentType(mimeType.getMimeType());
    }
    final static String[] defaultValidMimeTypes_ = {
            "image/png",
            "image/x-png",
            "image/jpeg",
            "image/pjpeg",
            "image/gif",
            "application/octet-stream"
    };
    
    static String getMimeType(String contentType, String fileName, String[] validMimeTypes) {
        //Content-Typeが最優先
        if (contentType == null) {
            //次にファイル名
            IMimeType mimeType = getMimeTypeLogic().getMimeTypeByFileName(fileName);
            if (mimeType == null) {
                //それでもダメならapplication/octet-stream
                contentType = "application/octet-stream";
            } else {
                contentType = mimeType.getMimeType();
            }
        }
        
        String mimeType = contentType;
        int pos = contentType.indexOf(";");
        if (pos >= 0) {
            mimeType = contentType.substring(0, pos);
        }

        if (validMimeTypes == null) {
            validMimeTypes = defaultValidMimeTypes_;
        }
        for (String validMimeType : validMimeTypes) {
            if (validMimeType.compareTo(mimeType) == 0) {
                return mimeType;
            }
        }
        return null;
    }
    public static IServerFile toServerFile(FormFile formFile, String[] validMimeTypes, String charset) throws ApplicationException {
        String fileName = formFile.getFileName();
        String contentType = formFile.getContentType();
        String mimeType = getMimeType(contentType, fileName, null);
        if (mimeType == null) {
            //TODO
            throw new ApplicationException("");
        }
        
        int size = formFile.getFileSize();
        String maxSize = getCommonProperties().getProperty("upload.maxSize");
        if (size > Integer.parseInt(maxSize) * 1024) {
            //TODO
            throw new ApplicationException("");
        }
        
        InputStream inputStream = null;
        try {
            inputStream = formFile.getInputStream();
            if (inputStream == null) {
                return null;
            }
        } catch(IOException cause) {
            //TODO
            ApplicationException e = new ApplicationException("");
            e.initCause(cause);
            throw e;
        }
        
        IServerFile serverFile = (IServerFile)getContainer().getComponent(IServerFile.class);
        serverFile.setName(formFile.getFileName());
        serverFile.setMime(mimeType);
        serverFile.setSize(size);
        serverFile.setInputStream(inputStream);
        serverFile.setContentType(contentType);
        serverFile.setCharset(charset);
        
        return serverFile;
    }
    static String getRoot(String root) throws ApplicationException {
        if (root == null) {
            root_ = getCommonProperties().getProperty("files.dir");
        } else {
            root_ = root;
        }
        File dir = new File(root_);
        if (!dir.isDirectory() || !dir.canWrite()) {
            //TODO
            ApplicationException e = new ApplicationException("");
            e.setCauseArgs(dir.getPath());
            throw e;
        }
        return root_;
    }
    public static String formatPath(String rootDir, String baseDir, long fileId, boolean mkdir) throws ApplicationException {
        StringBuilder dirs = new StringBuilder(getRoot(rootDir));
        dirs.append(File.separator);
        dirs.append(baseDir.toLowerCase());
        dirs.append(File.separator);
        String fileName = String.valueOf(fileId);
        int length = fileName.length();
        int limit;
        if (length > 6) {
            limit = length - 6;
            dirs.append(fileName.substring(0, limit));
            dirs.append(File.separator );
            dirs.append(fileName.substring(limit, 3));
            fileName = fileName.substring(limit + 3, 3);
        } else if (length > 3) {
            limit = length - 3;
            dirs.append("0");
            dirs.append(File.separator);
            dirs.append(fileName.substring(0, limit));
            fileName = fileName.substring(limit, 3);
        } else {
            dirs.append("0");
            dirs.append(File.separator);
            dirs.append("0");
        }
        File dir = new File(dirs.toString());
        if (mkdir) {
            if (!dir.isDirectory()) {
                if (!dir.mkdirs()) {
                    //TODO
                    ApplicationException e = new ApplicationException("");
                    e.setCauseArgs(dir.getPath());
                    throw e;
                }
            }
            if (!dir.canWrite()) {
                //TODO
                ApplicationException e = new ApplicationException("");
                e.setCauseArgs(dir.getPath());
                throw e;
            }
        } else {
            if (!dir.isDirectory() || !dir.canWrite()) {
                //TODO
                ApplicationException e = new ApplicationException("");
                e.setCauseArgs(dir.getPath());
                throw e;
            }
        }
        dirs.append(File.separator);
        dirs.append(fileName);
        String pathName = dirs.toString();
        File file = new File(pathName);
        if (file.isFile() && !file.canWrite()) {
            //TODO
            ApplicationException e = new ApplicationException("");
            e.setCauseArgs(file.getName());
            throw e;
        }
        return pathName;
    }
}
