package com.armundia.filters;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.codec.binary.Base64InputStream;

public class MutableHttpServletRequest extends HttpServletRequestWrapper {
	static final Logger log = Logger.getLogger(MutableHttpServletRequest.class.getName());

    private final Map<String, String> customHeaders;    
    private boolean doZip;
    private boolean doBase64;

	public void setDoBase64(boolean doBase64) {
		this.doBase64 = doBase64;
	}

	public MutableHttpServletRequest(HttpServletRequest request, boolean doBase64, boolean doZip){
        super(request);
        this.customHeaders = new HashMap<String, String>();
        this.doBase64 = doBase64;
        this.doZip = doZip;
        
        if ( doZip || doBase64 ) {
        	this.modifyHeader("Content-Type", "application/json; charset=utf-8");
        }
    }
    
    @Override
	public String getContentType() {
		String ct =  getHeader("Content-Type");
		if ( ct != null) return ct;
		else return super.getContentType();
	}

	public void modifyHeader(String name, String value) {
    	this.customHeaders.put(name, value);
    }
    
    public void putHeader(String name, String value){
        this.customHeaders.put(name, value);
    }
    
    
    @Override
	public Enumeration<String> getHeaders(String name) {
    	String headerValue = this.customHeaders.get(name);    	
    	if ( headerValue != null) {
    		Set<String> set = new HashSet<String>();
    		set.add(headerValue);
    		return Collections.enumeration(set);
    	} else {
    		return super.getHeaders(name);
    	}
	}

	@Override
    public String getHeader(String name) {
        String headerValue = customHeaders.get(name);        
        if (headerValue != null){
            return headerValue;
        }

        return ((HttpServletRequest) getRequest()).getHeader(name);
    }
 
    @Override
    public Enumeration<String> getHeaderNames() {
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            String n = e.nextElement();
            
            if (!set.contains(n)) {            
            	set.add(n);
            }
        }
        return Collections.enumeration(set);
    }
    
    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new ArmuServletInputStream(super.getInputStream(), doZip, doBase64);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }    
	
	private class ArmuServletInputStream extends ServletInputStream {
        private ServletInputStream input;
        private InputStream base64Input;
        private InputStream gzipInputstream;

        public ArmuServletInputStream(ServletInputStream input, boolean doZip, boolean doBase64) throws IOException {
            this.input = input;
            
            // ByteArrayOutputStream baos = new ByteArrayOutputStream();		
            
            if ( doZip ) {
            	
            	base64Input = new Base64InputStream(input);
            	gzipInputstream = new GZIPInputStream(base64Input);            	
//            	org.apache.commons.io.IOUtils.copy(gzipInputstream, baos);
            	
            } else if ( doBase64 ) {
            	base64Input = new Base64InputStream(input);
//            	org.apache.commons.io.IOUtils.copy(base64Input, baos);
            }
            					
//            byte[] bytes2 = baos.toByteArray();            
//            String decodedText = new BufferedReader(
//            	      new InputStreamReader(new ByteArrayInputStream(bytes2)))
//            	        .lines()
//            	        .collect(Collectors.joining("\n"));
//            
//            log.fine(() -> "CONTENT decoded: " + decodedText);
            
//            base64Input = new ByteArrayInputStream(bytes2);
//            gzipInputstream = new ByteArrayInputStream(bytes2);            
        }

        @Override
        public int read() throws IOException {
        	if ( doZip ) {
        		return gzipInputstream.read();
        	} else if ( doBase64 ) {
        		return base64Input.read();
        	} else {        	
        		return input.read();
        	}
        }

		@Override
		public boolean isFinished() {
			return input.isFinished();
		}

		@Override
		public boolean isReady() {
			return input.isReady();
		}

		@Override
		public void setReadListener(ReadListener readListener) {
			input.setReadListener(readListener);
		}
    }

}
