/*
 *  Copyright 2008 biaoping.yin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.frameworkset.util;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.frameworkset.util.tokenizer.StringTokenizer;
import org.frameworkset.util.tokenizer.Token;
import org.frameworkset.util.tokenizer.Tokenizer;
import org.frameworkset.util.tokenizer.TokenizerSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uwyn.jhighlight.renderer.Renderer;
import com.uwyn.jhighlight.renderer.XhtmlRendererFactory;
/**
 * <p>Title: CodeUtils.java</p> 
 * <p>Description: </p>
 * <p>bboss workgroup</p>
 * <p>Copyright (c) 2008</p>
 * @Date 2011-3-6
 * @author biaoping.yin
 * @version 1.0
 */
public class CodeUtils {


	

	/**
	 * Misc. static helper methods.
	 * <p/>
	 * Author: Vlad Ilyushchenko
	 */
	

	    private static Logger logger = LoggerFactory.getLogger(CodeUtils.class);

	 

	    /**
	     * Reads a file on disk. The method uses default file encoding (see: file.encoding system property)
	     *
	     * @param file to be read
	     * @return String representation of the file contents
	     */
	    public static String readFile(File file, String charsetName) throws IOException {
	        String result = null;
	        FileInputStream fis = new FileInputStream(file);
	        try {
	            result = readStream(fis, charsetName);
	        } finally {
	            fis.close();
	        }
	        return result;
	    }

	    /**
	     * Reads strings from the intput stream using the given charset. This method closes the input stream after
	     * it has been consumed.
	     *
	     * @param is
	     * @param charsetName
	     * @return
	     * @throws IOException
	     */
	    public static String readStream(InputStream is, String charsetName) throws IOException {

	        //
	        // use system's default encoding if the passed encoding is unsupported
	        //
	        Charset charset = Charset.forName(System.getProperty("file.encoding"));
	        if (Charset.isSupported(charsetName)) {
	            charset = Charset.forName(charsetName);
	        }

	        StringBuilder out = new StringBuilder();
	        BufferedReader r = new BufferedReader(new InputStreamReader(is, charset), 4096);
	        try {
	            String b;
	            while ((b = r.readLine()) != null) {
	                out.append(b).append("\n");
	            }
	        } finally {
	            r.close();
	        }

	        return out.toString();
	    }

	    public static void delete(File f) {
	        if (f != null && f.exists()) {
	            if (f.isDirectory()) {
	                File files[] = f.listFiles();
	                for (int i = 0; i < files.length; i++) {
	                    delete(files[i]);
	                }
	            }
	            if (!f.delete()) {
	                logger.debug("Cannot delete " + f.getAbsolutePath());
	            }
	        } else {
	            logger.debug(f + " does not exist");
	        }
	    }

	    public static int toInt(String num, int defaultValue) {
	        try {
	            return Integer.parseInt(num);
	        } catch (NumberFormatException e) {
	            return defaultValue;
	        }
	    }

	    public static int toIntHex(String num, int defaultValue) {
	        try {
	            if (num != null && num.startsWith("#")) num = num.substring(1);
	            return Integer.parseInt(num, 16);
	        } catch (NumberFormatException e) {
	            return defaultValue;
	        }
	    }

	    public static int toInt(Integer num, int defaultValue) {
	        return num == null ? defaultValue : num.intValue();
	    }

	    public static long toLong(Long num, long defaultValue) {
	        return num == null ? defaultValue : num.longValue();
	    }

	    public static String getJSPEncoding(InputStream is) throws IOException {

	        String encoding = null;
	        String contentType = null;

	        Tokenizer jspTokenizer = new Tokenizer();
	        jspTokenizer.addSymbol("\n", true);
	        jspTokenizer.addSymbol(" ", true);
	        jspTokenizer.addSymbol("\t", true);
	        jspTokenizer.addSymbol(new TokenizerSymbol("dir", "<%@", "%>", false, false, true, false));

	        StringTokenizer directiveTokenizer = new StringTokenizer();
	        directiveTokenizer.addSymbol("\n", true);
	        directiveTokenizer.addSymbol(" ", true);
	        directiveTokenizer.addSymbol("\t", true);
	        directiveTokenizer.addSymbol("=");
	        directiveTokenizer.addSymbol("\"", "\"", false);
	        directiveTokenizer.addSymbol("'", "'", false);

	        StringTokenizer contentTypeTokenizer = new StringTokenizer();
	        contentTypeTokenizer.addSymbol(" ", true);
	        contentTypeTokenizer.addSymbol(";", true);


	        Reader reader = new InputStreamReader(is, "ISO-8859-1");
	        try {
	            jspTokenizer.setReader(reader);
	            while (jspTokenizer.hasMore()) {
	                Token token = jspTokenizer.nextToken();
	                if ("dir".equals(token.getName())) {
	                    directiveTokenizer.setString(token.getInnerText());
	                    if (directiveTokenizer.hasMore() && directiveTokenizer.nextToken().getText().equals("page")) {
	                        while (directiveTokenizer.hasMore()) {
	                            Token dTk = directiveTokenizer.nextToken();
	                            if ("pageEncoding".equals(dTk.getText())) {
	                                if (directiveTokenizer.hasMore() && "=".equals(directiveTokenizer.nextToken().getText()))
	                                {
	                                    if (directiveTokenizer.hasMore()) {
	                                        encoding = directiveTokenizer.nextToken().getInnerText();
	                                        break;
	                                    }
	                                }
	                            } else if ("contentType".equals(dTk.getText())) {
	                                if (directiveTokenizer.hasMore() && "=".equals(directiveTokenizer.nextToken().getText()))
	                                {
	                                    if (directiveTokenizer.hasMore()) {
	                                        contentType = directiveTokenizer.nextToken().getInnerText();
	                                    }
	                                }
	                            }
	                        }
	                    }
	                }
	            }
	        } finally {
	            reader.close();
	        }

	        if (encoding == null && contentType != null) {
	            contentTypeTokenizer.setString(contentType);
	            while (contentTypeTokenizer.hasMore()) {
	                String token = contentTypeTokenizer.nextToken().getText();
	                if (token.startsWith("charset=")) {
	                    encoding = token.substring("charset=".length());
	                    break;
	                }
	            }
	        }

	        return encoding != null ? encoding : "ISO-8859-1";
	    }

	    public static void sendFile(HttpServletRequest request, HttpServletResponse response, File file) throws IOException {
	        OutputStream out = response.getOutputStream();
	        RandomAccessFile raf = new RandomAccessFile(file, "r");
	        try {
	            long fileSize = raf.length();
	            long rangeStart = 0;
	            long rangeFinish = fileSize - 1;

	            // accept attempts to resume download (if any)
	            String range = request.getHeader("Range");
	            if (range != null && range.startsWith("bytes=")) {
	                String pureRange = range.replaceAll("bytes=", "");
	                int rangeSep = pureRange.indexOf("-");

	                try {
	                    rangeStart = Long.parseLong(pureRange.substring(0, rangeSep));
	                    if (rangeStart > fileSize || rangeStart < 0) rangeStart = 0;
	                } catch (NumberFormatException e) {
	                    // ignore the exception, keep rangeStart unchanged
	                }

	                if (rangeSep < pureRange.length() - 1) {
	                    try {
	                        rangeFinish = Long.parseLong(pureRange.substring(rangeSep + 1));
	                        if (rangeFinish < 0 || rangeFinish >= fileSize) rangeFinish = fileSize - 1;
	                    } catch (NumberFormatException e) {
	                        // ignore the exception
	                    }
	                }
	            }

	            // set some headers
	            response.setContentType("application/x-download");
	            response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
	            response.setHeader("Accept-Ranges", "bytes");
	            response.setHeader("Content-Length", Long.toString(rangeFinish - rangeStart + 1));
	            response.setHeader("Content-Range", "bytes " + rangeStart + "-" + rangeFinish + "/" + fileSize);

	            // seek to the requested offset
	            raf.seek(rangeStart);

	            // send the file
	            byte buffer[] = new byte[4096];

	            long len;
	            int totalRead = 0;
	            boolean nomore = false;
	            while (true) {
	                len = raf.read(buffer);
	                if (len > 0 && totalRead + len > rangeFinish - rangeStart + 1) {
	                    // read more then required?
	                    // adjust the length
	                    len = rangeFinish - rangeStart + 1 - totalRead;
	                    nomore = true;
	                }

	                if (len > 0) {
	                    out.write(buffer, 0, (int) len);
	                    totalRead += len;
	                    if (nomore) break;
	                } else {
	                    break;
	                }
	            }
	        } finally {
	            raf.close();
	        }
	    }

	    public static String highlightStream(String name, File input, String rendererName, String encoding) throws IOException {
	    	InputStream input_ = null;
	    	try
	    	{
		    	input_ = new FileInputStream(input);
		    	return  highlightStream( name, input_,  rendererName,  encoding);
	    	}
	    	finally
	    	{
	    		if(input_ != null)
	    			input_.close();
	    	}
	    }
	    
	    public static String highlightStream(String name, String input, String rendererName, String encoding) throws IOException {
	    	InputStream input_ = null;
	    	try
	    	{
		    	input_ = new FileInputStream(input);
		    	return  highlightStream( name, input_,  rendererName,  encoding);
	    	}
	    	finally
	    	{
	    		if(input_ != null)
	    			input_.close();
	    	}
	    }

	    public static String highlightStream(String name, InputStream input, String rendererName, String encoding) throws IOException {

	        Renderer jspRenderer = XhtmlRendererFactory.getRenderer(rendererName);
	        if (jspRenderer != null) {
	            ByteArrayOutputStream bos = new ByteArrayOutputStream();
	            jspRenderer.highlight(name, input, bos, encoding, true);

	            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

	            Tokenizer tokenizer = new Tokenizer(new InputStreamReader(bis, encoding));
	            tokenizer.addSymbol(new TokenizerSymbol("EOL", "\n", null, false, false, true, false));
	            tokenizer.addSymbol(new TokenizerSymbol("EOL", "\r\n", null, false, false, true, false));

	            //
	            // JHighlight adds HTML comment as the first line, so if
	            // we number the lines we could end up with a line number and no line
	            // to avoid that we just ignore the first line alltogether.
	            //
	            StringBuilder buffer = new StringBuilder();
	            long counter = 0;
	            while (tokenizer.hasMore()) {
	                Token tk = tokenizer.nextToken();
	                if ("EOL".equals(tk.getName())) {
	                    counter++;
	                    buffer.append(tk.getText());
	                } else if (counter > 0) {
	                    buffer.append("<span class=\"codeline\">");
	                    buffer.append("<span class=\"linenum\">");
	                    buffer.append(leftPad(Long.toString(counter), 6, "&nbsp;"));
	                    buffer.append("</span>");
	                    buffer.append(tk.getText());
	                    buffer.append("</span>");
	                }
	            }
	            return buffer.toString();
	        }
	        return null;
	    }

	    public static String leftPad(String s, int len, String fill) {
	        StringBuilder sb = new StringBuilder(len);
	        if (s.length() < len) {
	            for (int i = s.length(); i < len; i++) {
	                sb.append(fill);
	            }
	        }
	        sb.append(s);
	        return sb.toString();
	    }

	    public static List getNamesForLocale(String baseName, Locale locale) {
	        List result = new ArrayList(3);
	        String language = locale.getLanguage();
	        String country = locale.getCountry();
	        String variant = locale.getVariant();
	        StringBuilder temp = new StringBuilder(baseName);

	        if (language.length() > 0) {
	            temp.append('_').append(language);
	            result.add(0, temp.toString());
	        }

	        if (country.length() > 0) {
	            temp.append('_').append(country);
	            result.add(0, temp.toString());
	        }

	        if (variant.length() > 0) {
	            temp.append('_').append(variant);
	            result.add(0, temp.toString());
	        }

	        return result;
	    }

	 
	

}
