/**
 * 
 */
package com.salesmanager.core.business.modules.cms.content.local;

import java.io.IOException;
import java.net.URLConnection;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.salesmanager.core.business.constants.Constants;
import com.salesmanager.core.business.exception.ServiceException;
import com.salesmanager.core.business.modules.cms.content.FileGet;
import com.salesmanager.core.business.modules.cms.content.FilePut;
import com.salesmanager.core.business.modules.cms.content.FileRemove;
import com.salesmanager.core.business.modules.cms.impl.LocalCacheManagerImpl;
import com.salesmanager.core.model.content.FileContentType;
import com.salesmanager.core.model.content.InputContentFile;
import com.salesmanager.core.model.content.OutputContentFile;



/**
 * Manages
 * - Images
 * - Files (js, pdf, css...) on a local web server
 * @author Carl Samson
 * @since 1.0.3
 *
 */
public class CmsStaticContentFileManagerImpl implements FilePut,FileGet,FileRemove
{

    private static final Logger LOGGER = LoggerFactory.getLogger( CmsStaticContentFileManagerImpl.class );
    private static CmsStaticContentFileManagerImpl fileManager = null;
    private static final String ROOT_NAME="static";
    
    private static final String ROOT_CONTAINER = "files";
    
    private String rootName = ROOT_NAME;
    
    private LocalCacheManagerImpl cacheManager;
    
 

    public static CmsStaticContentFileManagerImpl getInstance()
    {

       if ( fileManager == null )
        {
            fileManager = new CmsStaticContentFileManagerImpl();
        }

        return fileManager;

    }

    
    /**
     * <p>Method to add static content data for given merchant.Static content data can be of following type
     * <pre>
     * 1. CSS and JS files
     * 2. Digital Data like audio or video
     * </pre>
     * </p>
     * <p>
     * Merchant store code will be used to create cache node where merchant data will be stored,input data will
     * contain name, file as well type of data being stored.
     * @see FileContentType
     * </p>
     *  
     * @param merchantStoreCode merchant store for whom data is being stored
     * @param inputStaticContentData data object being stored
     * @throws ServiceException
     * 
     */
    @Override
    public void addFile( final String merchantStoreCode, final InputContentFile inputStaticContentData )
        throws ServiceException
    {
/*        if ( cacheManager.getTreeCache() == null )
        {
            LOGGER.error( "Unable to find cacheManager.getTreeCache() in Infinispan.." );
            throw new ServiceException( "CmsStaticContentFileManagerInfinispanImpl has a null cacheManager.getTreeCache()" );
        }*/
        try
        {
            
    		
        	
            //base path
        	String rootPath = this.buildRootPath();
        	Path confDir = Paths.get(rootPath); 
        	this.createDirectoryIfNorExist(confDir);
        	
        	//node path
        	StringBuilder nodePath = new StringBuilder();
        	nodePath
        	.append(rootPath)
        	.append(merchantStoreCode);
        	Path merchantPath = Paths.get(nodePath.toString()); 
        	this.createDirectoryIfNorExist(merchantPath);
        	
        	//file path
        	nodePath.append(Constants.SLASH).append( inputStaticContentData.getFileContentType()).append(Constants.SLASH);
        	Path dirPath = Paths.get(nodePath.toString()); 
        	this.createDirectoryIfNorExist(dirPath);

        	//file creation
        	nodePath.append(inputStaticContentData.getFileName());
        	
        	
        	Path path = Paths.get(nodePath.toString());
        	
        	
        	//file creation
        	//nodePath.append(Constants.SLASH).append(contentImage.getFileName());
        	
        	
        	//Path path = Paths.get(nodePath.toString());


            Files.copy(inputStaticContentData.getFile(), path, StandardCopyOption.REPLACE_EXISTING);
        	
        	
        	
        	
        	
        	//String nodePath = this.getNodePath(merchantStoreCode, inputStaticContentData.getFileContentType());
        	
    		//final Node<String, Object> merchantNode = this.getNode(nodePath);
    		
    		//merchantNode.put(inputStaticContentData.getFileName(), IOUtils.toByteArray( inputStaticContentData.getFile() ));
            
            LOGGER.info( "Content data added successfully." );
        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while saving static content data", e );
            throw new ServiceException( e );

        }
        
    }

    /**
     * <p>
     * Method to add files for given store.Files will be stored in Infinispan and will be retrieved based on
     * the storeID. Following steps will be performed to store static content files
     * </p>
     * <li>Merchant Node will be retrieved from the cacheTree if it exists else new node will be created.</li> <li>
     * Files will be stored in StaticContentCacheAttribute , which eventually will be stored in Infinispan</li>
     * 
     * @param merchantStoreCode Merchant store for which files are getting stored in Infinispan.
     * @param inputStaticContentDataList input static content file list which will get {@link InputContentImage} stored
     * @throws ServiceException if content file storing process fail.
     * @see InputStaticContentData
     * @see StaticContentCacheAttribute
     */
    @Override
    public void addFiles( final String merchantStoreCode, final List<InputContentFile> inputStaticContentDataList )
        throws ServiceException
    {
/*        if ( cacheManager.getTreeCache() == null )
        {
            LOGGER.error( "Unable to find cacheManager.getTreeCache() in Infinispan.." );
            throw new ServiceException( "CmsStaticContentFileManagerInfinispanImpl has a null cacheManager.getTreeCache()" );
        }*/
        try
        {
          
        	
            //base path
        	String rootPath = this.buildRootPath();
        	Path confDir = Paths.get(rootPath); 
        	this.createDirectoryIfNorExist(confDir);
        	
        	//node path
        	StringBuilder nodePath = new StringBuilder();
        	nodePath
        	.append(rootPath)
        	.append(merchantStoreCode);
        	Path merchantPath = Paths.get(nodePath.toString()); 
        	this.createDirectoryIfNorExist(merchantPath);
        	

        	
          for(final InputContentFile inputStaticContentData:inputStaticContentDataList){
 

          		//file path
          		nodePath.append(Constants.SLASH).append( inputStaticContentData.getFileContentType()).append(Constants.SLASH);
          		Path dirPath = Paths.get(nodePath.toString()); 
          		this.createDirectoryIfNorExist(dirPath);
          		
            	//file creation
          		nodePath.append(Constants.SLASH).append(inputStaticContentData.getFileName());
            	
            	
            	Path path = Paths.get(nodePath.toString());
          		
                Files.copy(inputStaticContentData.getFile(), path, StandardCopyOption.REPLACE_EXISTING);
        	  
        	  
          		//String nodePath = this.getNodePath(merchantStoreCode, inputStaticContentData.getFileContentType());
    		  
          		//final Node<String, Object> merchantNode = this.getNode(nodePath);
          		//merchantNode.put(inputStaticContentData.getFileName(), IOUtils.toByteArray( inputStaticContentData.getFile() ));
            

            }
          
          
            
            LOGGER.info( "Total {} files added successfully.",inputStaticContentDataList.size() );

        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while saving content image", e );
            throw new ServiceException( e );

        }
     }
 

    /**
     * Method to return static data for given Merchant store based on the file name. Content data will be searched
     * in underlying Infinispan cache tree and {@link OutputStaticContentData} will be returned on finding an associated
     * file. In case of no file, null be returned.
     * 
     * @param store Merchant store
     * @param contentFileName name of file being requested
     * @return {@link OutputStaticContentData}
     * @throws ServiceException
     */
    @Override
    public OutputContentFile getFile( final String merchantStoreCode, final FileContentType fileContentType, final String contentFileName )
        throws ServiceException
    {
       
/*        if ( cacheManager.getTreeCache() == null )
        {
            throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" );
        }
        OutputContentFile outputStaticContentData=null;
        InputStream input = null;
        try
        {
            
        	
    		String nodePath = this.getNodePath(merchantStoreCode, fileContentType);
        	
    		final Node<String, Object> merchantNode = this.getNode(nodePath);
    		

            final byte[] fileBytes= (byte[]) merchantNode.get( contentFileName );
            
            if ( fileBytes == null )
            {
                LOGGER.warn( "file byte is null, no file found" );
                return null;
            }

            input=new ByteArrayInputStream( fileBytes );
           
            final ByteArrayOutputStream output = new ByteArrayOutputStream();
            IOUtils.copy( input, output );
            
            outputStaticContentData=new OutputContentFile();
            outputStaticContentData.setFile( output );
            outputStaticContentData.setMimeType( URLConnection.getFileNameMap().getContentTypeFor(contentFileName) );
            outputStaticContentData.setFileName( contentFileName );
            outputStaticContentData.setFileContentType( fileContentType );
            
        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode);
            throw new ServiceException( e );
        }
       return outputStaticContentData != null ? outputStaticContentData : null;*/
    	
    	
    	return null;
    	
    }
    
    
	@Override
	public List<OutputContentFile> getFiles(
			final String merchantStoreCode, final FileContentType staticContentType) throws ServiceException {

		
/*		
        if ( cacheManager.getTreeCache() == null )
        {
            throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" );
        }
        List<OutputContentFile> images = new ArrayList<OutputContentFile>();
        try
        {
            
        	FileNameMap fileNameMap = URLConnection.getFileNameMap();
    		String nodePath = this.getNodePath(merchantStoreCode, staticContentType);
        	
    		final Node<String, Object> merchantNode = this.getNode(nodePath);
    		
            for(String key : merchantNode.getKeys()) {
            	
                byte[] imageBytes = (byte[])merchantNode.get( key );

                OutputContentFile contentImage = new OutputContentFile();

                InputStream input = new ByteArrayInputStream( imageBytes );
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                IOUtils.copy( input, output );

                String contentType = fileNameMap.getContentTypeFor( key );

                contentImage.setFile( output );
                contentImage.setMimeType( contentType );
                contentImage.setFileName( key );

                images.add( contentImage );
            	
            	
            }
            

            
        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode);
            throw new ServiceException( e );
        }

		
		return images;*/
		
		return null;
		
		
	}
    
    

    @Override
    public void removeFile( final String merchantStoreCode, final FileContentType staticContentType, final String fileName )
        throws ServiceException
    {

        try
        {
            
        	
			StringBuilder merchantPath = new StringBuilder();
	        merchantPath.append(buildRootPath())
	        .append(Constants.SLASH)
	        .append(merchantStoreCode)
	        .append(Constants.SLASH)
	        .append(staticContentType)
	        .append(Constants.SLASH)
	        .append(fileName);
	        
	        Path path = Paths.get(merchantPath.toString());
	        
	        Files.deleteIfExists(path);
        	


        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while deleting files for {} merchant ", merchantStoreCode);
            throw new ServiceException( e );
        }

        
    }

    /**
     * Removes the data in a given merchant node
     */
    @SuppressWarnings("unchecked")
	@Override
    public void removeFiles( final String merchantStoreCode )
        throws ServiceException
    {
        
        LOGGER.debug( "Removing all images for {} merchant ",merchantStoreCode);

        try
        {
            

			StringBuilder merchantPath = new StringBuilder();
	        merchantPath.append(buildRootPath())
	        .append(Constants.SLASH)
	        .append(merchantStoreCode);
	        
	        Path path = Paths.get(merchantPath.toString());
	        
	        Files.deleteIfExists(path);
        	
        	


        }
        catch ( final Exception e )
        {
            LOGGER.error( "Error while deleting content image for {} merchant ", merchantStoreCode);
            throw new ServiceException( e );
        }

    }



    /**
     * Queries the CMS to retrieve all static content files. Only the name of the file will be returned to the client
     * @param merchantStoreCode
     * @return
     * @throws ServiceException
     */
	@Override
	public List<String> getFileNames(final String merchantStoreCode, final FileContentType staticContentType)
			throws ServiceException {
		
		

	        try
	        {

				StringBuilder merchantPath = new StringBuilder();
		        merchantPath.append(buildRootPath())
		        .append(merchantStoreCode)
		        .append(Constants.SLASH)
		        .append(staticContentType);
		        
		        Path path = Paths.get(merchantPath.toString());
		        
		        List<String> fileNames = null;
		        
		        if(Files.exists(path)) {
		        
			        fileNames = new ArrayList<String>();
			        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
			        for (Path dirPath : directoryStream) {
			        	
			        		String fileName = dirPath.getFileName().toString();
			        	
			        		if(staticContentType.name().equals(FileContentType.IMAGE.name())) {
			        	        //File f = new File(fileName);
			        	        String mimetype = URLConnection.guessContentTypeFromName(fileName);
			        	        //String mimetype= new MimetypesFileTypeMap().getContentType(f);
			        	        if(!StringUtils.isBlank(mimetype)) {
				        	        String type = mimetype.split("/")[0];
				        	        if(type.equals("image")) {
				        	        	fileNames.add(fileName);
				        			}
			        	        }
			        	        //fileNames.add(fileName);
      
			        		} else {
			        			fileNames.add(fileName);
			        		}
		        
			        }
		        }

	    		return fileNames;

	        } catch ( final Exception e ) {
	            LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode);
	            throw new ServiceException( e );
	        }

	}

	public void setRootName(String rootName) {
		this.rootName = rootName;
	}

	public String getRootName() {
		return rootName;
	}
	
	private String buildRootPath() {
		return new StringBuilder().append(getRootName()).append(Constants.SLASH).append(ROOT_CONTAINER).append(Constants.SLASH).toString();

	}
	

	private void createDirectoryIfNorExist(Path path) throws IOException {

    	if (Files.notExists(path)) {
    		Files.createDirectory(path);
    	}
	}


	public LocalCacheManagerImpl getCacheManager() {
		return cacheManager;
	}


	public void setCacheManager(LocalCacheManagerImpl cacheManager) {
		this.cacheManager = cacheManager;
	}



}
