/*
 * Copyright 2008-2014 Mirko Solazzi - OBEROn Platform [www.oberonplatform.com]
 *
 * 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 com.oberon.client;

import java.io.*;
import java.util.*;

import javax.servlet.http.HttpSession;

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;

import com.oberon.client.ApplicationSession;
import com.oberon.client.Configuration;
import com.oberon.client.charts.DrawChart;
import com.oberon.client.graphics.GCWrapper;
import com.oberon.installer.Installer;
import com.oberon.ooql.connection.ConnectionManager;
import com.oberon.ooql.connection.DBConnection;
import com.oberon.ooql.connection.DriverManager;
import com.oberon.ooql.connection.OBConnection;
import com.oberon.ooql.connection.SqlConn;
import com.oberon.ooql.parser.ParserCreationException;
import com.oberon.ooql.parser.ParserLogException;
import com.oberon.ooql.sdk.*;
import com.oberon.ooql.sdk.Dictionary;
import com.oberon.ooql.sdk.Link;
import com.oberon.ooql.sdk.Menu;
import com.oberon.util.FileUtils;
import com.oberon.util.StringUtils;

/**
 * Basic class for Desktop (SWT) Applications.
 * Includes connection and utility functions.
 *
 * @author   Mirko Solazzi
 * @version  5.0
 */
public abstract class Application {
	/** Icon path */
	private final static String iconPath="images/";
	/** Default separator token */
	public final static String sToken="<|>";
	
	// Web Links
	/** OBEROn Web Site */
	public final static String URL_OBERON = "http://www.oberonplatform.com/";
	/** OOQL syntax documentation web link */
	public final static String URL_OOQL = URL_OBERON+"ooql.php";
	/** Java API documentation web link */
	public final static String URL_JAVAAPI = URL_OBERON+"api/";

	// Display Parameters
	/** Application SWT display */
	public Display display;
	/** Application SWT shell */
	public Shell shell;	
	/** The Application title */
	public String name = "OBEROn Platform - Application Client";
	/** The Application subtitle */
	public String subname = "Object Based Enterprise Resource Organization";
	/** RWT Application flag */
	public static boolean isRWT=false;
	/** The Application icon */
	public Image appIcon = null;
	/** The Application default font */
	public static Font globalfont ;
	/** The Application dialog registry */
	public final Hashtable<String,Shell> dialogs = new Hashtable<String,Shell>() ;
	
	// User Context
	/** The Application session */
	public HttpSession session ;
	/** OBEROn user framework */
	public Framework framework;
	/** Default Locale */
	public Locale locale = Locale.ENGLISH;
	/** Select Application from a list */
	public boolean selectApplication = false;

	// Configuration and Connection
	/** Database JDBC driver manager */
	public static DriverManager driverManager;
	/** Configuration object */
	public Configuration conf;

	// Application Parameters (Defaults)
	/** The Application main menu */
	public String  application_MainMenu="Application";
	/** Dictionary section for Menu labels*/
	public String  dictionary_menu_section="Menu";
	/** Dictionary section for Common labels*/
	public String  dictionary_common_section="Common";
	/** Dictionary section for Application labels*/
	public String  dictionary_section="Application";
	/** The default dictionary language */
	public String  dictionary_default_language = "EN";
	/** Show the Object class in the result lists */
	public boolean show_object_class = true;
	/** Show the Object name / revision in the result lists */
	public boolean show_object_namerev = true;
	/** Use the search patterns for text fields */
	public boolean use_search_patterns = true;
	/** Use the fixed decimal separator */
	public String  number_separator_symbols = "";
	/** Default name-revision separator */
	public String  name_revision_separator = ".";	
	/** Label "All" for search selection item */
	public String  search_all_label = "All";
	// If add new parameters: update the MenuDlg class and createMenu method

	/** "ID" */
	public final static String ID = "ID";
	/** "FILE" */
	public final static String CONF_FILE_SECTION = "FILE";
	/** "VIEW" */
	public final static String CONF_VIEW_SECTION = "VIEW";
	/** "ON TOP options for dialog windows" */
	public static int ON_TOP = 0;
	
	private Vector<int[]> vCoords;		
	
	private int iWidth = 1000;
	private int iHeight = 600;
	
	private static ImageData imgDataOn;
	private static ImageData imgDataOff;		
	
	private Vector<String> vDownloadFiles = new Vector<String>(5);
	public  String uploadDir = System.getProperty("java.io.tmpdir");
	
	private static final Hashtable<Thread,Application> hThreads = new Hashtable<Thread,Application>();
	
	public Application () {
	}	

	public synchronized void setApp(Application application) {
		hThreads.put(Thread.currentThread(), application);
	}
	
	public static Application getApp() {
	   Application app = hThreads.get(Thread.currentThread());
	   if (app == null) { 
		   app=new BasicApplication(); 
		   app.setApp(app);
	   }
 	   return app;
    }
	
//---------------------------- CONFIGURATION FILE PROPERTIES ------------------------  

	/**
	 * Get a configuration property value
	 * 
	 * @param section   configuration file section
	 * @param property  property name
	 */
	public String getProperty(String section, String property) {
		if (conf!=null) {
			String sValue =  conf.getValue(section,property);
			if (sValue!=null) { return sValue; }
		}
		return "";	     
	}

	/**
	 * Get a configuration property value from the "CONNECTION" section
	 * 
	 * @param property  property name
	 */
	public String getConnectionProperty(String property) {
		if (conf!=null) {
			String sValue =  conf.getValue(OBConnection.CONN_SECTION,property);
			if (sValue!=null) { return sValue; }
		}
		return "";	     
	}
	
	//----------------------- DATABASE CONNECTIONS ---------------------------
	/**
	 *  Open the OBEROn Database/RMI/HTTP connections and perform the Login (open the Framework if user account is specified)
	 *  
	 *  @see ConnectionManager#openConnections(Application)
	 */
	public void openConnections() {
		try {
			session = new ApplicationSession();
			ConnectionManager.openConnections(this);
			boolean bInstalled=true;
			if (ConnectionManager.isDBConnection()) {	
				DBConnection conn = (DBConnection)getConnection();
				try {					
					SqlConn con = conn.getConnection();
					bInstalled=Installer.isInstalled(con);
				} finally {
					releaseConnection(conn);
				}
			}
			if (bInstalled)  {
				openFramework(getConnectionProperty(ConnectionManager.PROP_OBUSR), getConnectionProperty(ConnectionManager.PROP_OBPWD));
				uploadDir = ConnectionManager.getUploadDir();
				if (isRWT) ON_TOP=SWT.ON_TOP;
			} else {
				String sMessage = OberonMessages.getMessage("OberonNotInstalled", null);
				showMessage(shell, sMessage, "OBEROn", SWT.APPLICATION_MODAL | SWT.ICON_ERROR | SWT.OK);
				System.exit(0);
			}
			if (framework.getLanguage().length()==0) {
				framework.setLanguage(dictionary_default_language);
			}
			session.setAttribute(Arg.Language , framework.getLanguage() );
		} catch (NullPointerException e) {
			framework=null;
			//if (display!=null) { display.dispose(); }
		} catch (Exception ex) {
			framework=null;
			System.out.println(ex.getMessage());
			//if (display!=null) { display.dispose(); }
		} 
	}  
	
	
	/** Close all OBEROn connections
	 * 
	 * @since 4.1
	 */
	public void closeConnections() {	
		 ConnectionManager.closeConnections(this);
	}
	
	/**
	 * Get OBEROn connection   
	 *
	 * @see ConnectionManager#getConnection(Framework)
	 */
	public OBConnection getConnection() {
		if (framework!=null) framework.clearResult();
		return ConnectionManager.getConnection(framework);
	}
	
	/**
	 * Release an OBEROn connection   
	 * 
	 * @param conn The OBEROn connection 
	 * 
	 * @see ConnectionManager#releaseConnection(OBConnection,Framework)
	 */
	public void releaseConnection(OBConnection conn) {
		ConnectionManager.releaseConnection(conn,framework);
	}

	/**
	 * Check if the Application is connected to the Database or to RMI/HTTP server
	 * 
	 * @see ConnectionManager#isConnected()
	 */
	public boolean isConnected() {
		return ConnectionManager.isConnected();
	} 

	/**
	 * Check if the Connection is established directly to the DB
	 * 
	 * @see ConnectionManager#isDBConnection() 
	 */
	public boolean isDBConnection() {
		return ConnectionManager.isDBConnection();
	} 

	
	//---------------------------- OBEROn ACTIONS ------------------------  	
	
	/**
	 * Retrieve the current framework from the HttpSession
	 */
	public static Framework getFramework(HttpSession session) {
		return (Framework)session.getAttribute(Arg.Framework);
	}
	
	/**
	 * Save the current framework into the HttpSession
	 */
	public static void setFramework(HttpSession session,Framework framework) {
		session.setAttribute(Arg.Framework,framework);
	}
	
	/**
	 * Login into OBEROn
	 * 
	 * @param sUserName  the user name
	 * @param sPassword  the user password
	 */
	public boolean openFramework(String sUserName,String sPassword) throws Exception {
		if ( !isConnected() ) { 
			log("Not Connected!!");
			return false; 
		}
		if (StringUtils.isBlank(sUserName)) { 
			setFramework(); 
			return false; 
		}
		Framework preFramework=framework;
		framework=new Framework(sUserName,sPassword);
		if (preFramework==null) { preFramework=framework; }
		framework.setVerbose(false);
		framework.setSQLTrace(false);
		framework.setRenderingEngine("SWT");
		// link the framework
		try {
			framework.link();
			log("OBEROn Login for user '"+sUserName+"' successful!" );
			setFramework(session,framework);
			String sResultLog;
			if ((sResultLog=framework.getResultLog().trim()).length()>0) {	log(sResultLog);}
			framework.clearResultLog();
			return true;
		} catch (Exception e) {	
			if (preFramework.isLinked()) {  
				framework = preFramework;  
				setFramework(session,framework);
			}
			System.out.println(e.getMessage());
			log("OBEROn Login for user '"+sUserName+"' failed: "+e.getMessage() );
			setFramework();
		  return false;
		}
	}

	/**
	 * Perform one or more OBEROn OOQL commands with current application Framework
	 * 
	 * @param  sOOQLCommands  command text 
	 * @return a Vector of String(s) where each element represents the result for the corresponding command 
	 * 
	 */
	public Vector<String> performOOQLCommands( String sOOQLCommands ) throws OberonException, ParserLogException, ParserCreationException  {				
		if (framework==null || !framework.isLinked()) { throw new OberonException("Framework not Linked!"); }
		Vector<String> vRes;
		String sResultLog;
		OOQLCommand ooql = new OOQLCommand(sOOQLCommands );
		try {			
			vRes = ooql.execute(framework);
			setFramework(session,framework);
		} finally {
			vCoords=ooql.getCommandCoords();   
			if ((sResultLog=ooql.getResultLog().trim()).length()>0) {
				log("Result LOG:\n"+sResultLog);	
				framework.clearResultLog();
			}
		}
		return vRes;
	}      
	
	/**
	 * Parse the OOQL command text 
	 * 
	 * @param sOOQLCommands  command text 
	 * 
	 * @return a report representing the OOQL language parsed tree
	 */
	public String parseOOQLCommands(String sOOQLCommands ) throws OberonException, ParserLogException, ParserCreationException  {
		if (framework==null || !framework.isLinked()) { throw new OberonException("Framework not Linked!"); }
		String result;
		OOQLCommand ooql = new OOQLCommand(sOOQLCommands );
		try {			
			result = ooql.parse(framework);
			setFramework(session,framework);
		} finally {
			vCoords=ooql.getCommandCoords();   
		}
		return result;
	}  

	
	/**
	 * Get the command text coordinates for each OOQL command
	 * for the last performOOQLCommands execution 
	 * 
	 * @return Vector of int[4]{startLine,startColumn,endLine,endColumn}
	 */
	public Vector<int[]> getCommandCoords(){
		return vCoords;
	}

	/**
	 * Turn off the verbose option
	 */
	public void setNoVerbose() {
		if (framework!=null) {
			framework.setSQLTrace(false);
			framework.setVerbose(false);
			framework.setFilterHidden(false);
		}
	}

	/**
	 * Perform a selection of administrative objects
	 *
	 * @param results     SWT table to show the search result 
	 * @param vExclude    Vector of object names excluded from the result
	 * @param sTypeName   object type
	 * @param sFilter     filter pattern
	 * @param append        if true append the found objects to the table; otherwise replace the current content 
	 */
	public void performSearch(Table results,Vector<?> vExclude, String sTypeName, String sFilter,boolean append){
		performSearch( results, vExclude, "", sTypeName,  sFilter, append);
	}
	

	/**
	 * Perform a selection of administrative objects
	 *
	 * @param results       SWT table to show the search result 
	 * @param vExclude      Vector of object names excluded from the result
	 * @param sApplication  application name
	 * @param sTypeName     object type
	 * @param sFilter       filter pattern
	 * @param append        if true append the found objects to the table; otherwise replace the current content
	 */
	public void performSearch(Table results,Vector<?> vExclude, String sApplication,String sTypeName, String sFilter,boolean append){
		setNoVerbose();
		framework.clearResult();
		Vector<String> vRes;
		if (sTypeName.equals("application")) {	
			vRes=Menu.getApplications(framework);
			sTypeName="menu";
		} else {
			try {
				AdminQuery adminquery = new AdminQuery(sTypeName);				
				adminquery.setPatterns(StringUtils.StringTokensToVector(sFilter, ","));
				if (sApplication.length()>0) adminquery.setApplicationPattern(sApplication);
				adminquery.execute(framework);	
			} catch (Exception e) {
				log(e.getMessage());
				return;
			}			
			vRes=StringUtils.StringTokensToVector(framework.getResult().trim(),"\n");
		}
		if (!append) { results.removeAll(); }
		StringUtils.orderStringVector(vRes,true);
		results.setRedraw (false);
		int iSize = vRes.size();
		for (int i=0;i<iSize;i++){
			if (vExclude.indexOf(vRes.elementAt(i))<0 && vExclude.indexOf("+"+vRes.elementAt(i))<0) {
				TableItem item = new TableItem(results,SWT.NONE);
				item.setText(vRes.elementAt(i));
				try { item.setImage(getIcon(sTypeName.toLowerCase()+"_s.png"));} catch (Exception e){}
				setData(item,TAGS.TYPE,sTypeName);
			}
		}
		results.setRedraw (true);
	}   
	
	//----------------------------- IMAGE MANIPULATION ---------------------------------------

	/**
	 * Retrieve an image/icon from the OBEROn JAR package
	 * 
	 * @param iconName   the icon/image file name
	 */
	public static Image getIcon(String iconName) {	
		  if (getApp().display==null) getApp().display=new Display();
			return new Image(getApp().display,Application.class.getResourceAsStream(iconPath + iconName));
	}
	
	/**
	 * Convert and resize a byte[] image 
	 * 
	 * @param baImage   byte array image
	 * @param scx			  resize width (if 0 is ignored)
	 * @param scy       resize height (if 0 is ignored)
	 * 
	 * @return  SWT Image
	 */
	public static Image convertImage(byte[] baImage,int scx,int scy) {
		if (getApp().display==null) getApp().display=new Display();
		if (baImage!=null && baImage.length>0) {
			ByteArrayInputStream bS = new ByteArrayInputStream(baImage);
			ImageData img = new ImageData(bS);
			if (scx!=0 || scy!=0) {
				if (scx!=0 && scy!=0) {
					img=img.scaledTo(scx,scy);
				} else if (scx!=0) {
					img=img.scaledTo(scx, (int)Math.round(scy*scx/(float)img.width));
				} else if (scy!=0) {
					img=img.scaledTo((int)Math.round(scx*scy/(float)img.height),scy);
				} 
			}
			return new Image(getApp().display,img);
		} else { return null; }
	}
	
	/**
	 * Retrieve the administrative object icon from the OBEROn DB and resize it
	 * 
	 * @param sAdminType  administrative object type
	 * @param sAdminName  administrative object name
	 * @param scx			    resize width (if 0 is ignored)
	 * @param scy         resize height (if 0 is ignored)
	 * 
	 */
	public Image getImage(String sAdminType,String sAdminName ,int scx , int scy ) {
		try {
			return convertImage(AdminBase.getImage(sAdminType,sAdminName),scx,scy);
		} catch (Exception ex) {
			return null;
		}
	}

	
	/**
	 * Retrieve the administrative object icon using the internal ID and resize it
	 * 
	 * @param iAdminId    the internal ID
	 * @param scx			    resize width (if 0 is ignored)
	 * @param scy         resize height (if 0 is ignored)
	 */
	public Image getImage(int iAdminId ,int scx , int scy ) {
		try {
			return convertImage(AdminBase.getImage(iAdminId),scx,scy);
		} catch (Exception ex) {
			return null;
		}
	}

	/**
	 * Retrieve a object instance icon and resize it
	 * 
	 * @param sObjectID          the Object id
	 * @param scx			    resize width (if 0 is ignored)
	 * @param scy         resize height (if 0 is ignored)
	 */
	public Image getObjImage(String sObjectID,int scx,int scy ) {
		try {
			return convertImage(ObjectObj.getImage(sObjectID),scx,scy);
		} catch (Exception ex) {
			return null;
		}		
	}
	
	
	/**
	 * Create an image representing a progress bar
	 * [need the SWT jar installed]
	 * 
	 * @param value         the percentage of completion
	 * @param width         the image width
	 * @param height        the image height
	 *
	 * @since 2.2.02
	 * @return the progress bar image
	 *
	 */
	static  public byte[] createProgressBarImage(float value,int width, int height ,HttpSession httpSession) throws Exception {
		if (imgDataOn==null || imgDataOff==null) {
			initImages(width, height,httpSession);
		}
		try {
			if (getApp().display==null) getApp().display=new Display();
			ImageData imgData = imgDataOn.scaledTo(Math.min(width,1+(int)Math.floor( width*value / 100)),height);
			Image img = new Image(getApp().display,imgDataOff.scaledTo(width, height));
			GCWrapper gc = new GCWrapper(img);
			gc.drawImage(new Image(getApp().display,imgData), 0, 0);
			gc.dispose();  
			return DrawChart.encodePNGImage(gc.getUpdatedImage());
		} catch (Exception ex) { 
			throw OberonException.generateOberonException(ex,"createProgressBarImage("+value+","+width+","+height+")" );							
		}
	}

	private static void initImages(int width, int height,HttpSession session) {
		if (getApp().display==null) { getApp().display = new Display(); }
		String sImagesPath=session.getServletContext().getRealPath("/")+"resources";	
		try {			
			FileInputStream fs = new FileInputStream(new File(sImagesPath+"/progressOn.png"));
			imgDataOn = (new Image(getApp().display,fs)).getImageData();
			fs = new FileInputStream(new File(sImagesPath+"/progressOff.png"));
			imgDataOff = (new Image(getApp().display,fs)).getImageData();
			imgDataOff = imgDataOff.scaledTo(width,height);
		} catch (Exception ex) {
			PaletteData palette = new PaletteData(new RGB[] { getApp().display.getSystemColor(SWT.COLOR_GRAY).getRGB() , getApp().display.getSystemColor(SWT.COLOR_GREEN).getRGB() });
			imgDataOn  = new ImageData(width,height, 1, palette);
			for (int i = 0; i < width; i++) {
				for (int j = 0; j < height; j++) {
					imgDataOn.setPixel(i, j, 1);
				}
			}
			imgDataOff = new ImageData(width,height, 1 , palette);
		}
	}
	
	//----------------------------------- DISPLAY & UTILITY METHODS ---------------------

	/** Scale the element width based on the application window width*/
	public static int computeWidth(int width) {
		return (int)Math.ceil(width*getApp().iWidth/1000);
	}
	/** Scale the element height based on the application window height*/
	public static int computeHeight(int height) {
		return (int)Math.ceil(height*getApp().iHeight/600);
	}
	/** Set the application window width*/
	public static void setWindowWidth(int width) {
		getApp().iWidth=width;
	}
	/** Set the application window height*/
	public static void setWindowHeight(int heigth) {
		getApp().iHeight=heigth;
	}
	/** Get the application window width*/
	public static int getWindowWidth() {
		return getApp().iWidth;
	}
	/** Get the application window height*/
	public static int getWindowHeight() {
		return getApp().iHeight;
	}
	
	/**
	 * Load the Segoe standard Font from the JAR package
	 * 
	 */
	public static Font loadMonospacedFont(Display display) { 
		if (!isRWT) {
			try {
				String sFileName= "segoeui.ttf";
				String sFontName= "Segoe UI";
				String sTempDir = JLClient.getTempDir();
				int size=9;
				if (!new File(sTempDir+"/"+sFileName).exists()) {
					InputStream is = Application.class.getResourceAsStream(iconPath + sFileName) ;
					if (is!=null) {
						int Filelen = is.available();			
						byte[] bufferInput = new byte[Filelen] ;
						int offset = 0;
						int numRead = 0;
						while (offset < Filelen && (numRead=is.read(bufferInput, offset, bufferInput.length-offset)) >= 0) {
							offset += numRead;
						}
						is.close();	   
						FileUtils.createLocalDir(sTempDir);
						BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(sTempDir+"/"+sFileName), 4096);
						for (int i=0;i<bufferInput.length;i++) {
							os.write(bufferInput[i]);
						}
						os.close();
					}
				}		
				File file = new File(sTempDir+"/"+sFileName);
				if (file.exists()) { 
					if (!display.loadFont(file.getAbsolutePath())) {   
						System.out.println("Cannot load font file: "+file.toString());      
					} 				
				} else {     
					String jreHome = System.getProperty("java.home");        
					file = new File(jreHome, "/lib/fonts/LucidaSansRegular.ttf");
					if (file.exists()) { 
						if (!display.loadFont(file.getAbsolutePath())) {   
							System.out.println("Cannot load font file: "+file.toString());      
						} else {
							sFontName="Lucida Sans Regular";
							size=8;
						}				
					}
				}
				final Font font = new Font(display,sFontName, size, SWT.NORMAL);
				display.addListener(SWT.Dispose, new Listener() {                
					public void handleEvent(Event event) {                        
						font.dispose();                
					}        
				});  
				return font;
			} catch (Exception ex) {
				return display.getSystemFont();
			}
		} else {
			return display.getSystemFont();
		}		
	}
	
	/**
	 * Shows the OBEROn logo and info on start-up
	 */
	public static Shell showSplash(String sLogo) {
		//if (true) return null;
		if (getApp().display==null) getApp().display=new Display();
		Image logo=null;
		final Image image;
		final Shell splash = new Shell(SWT.ON_TOP);
		try {  logo = getIcon(sLogo); } catch (Exception e){ }
		int hpos=0;
		if (logo!=null) {
			hpos=logo.getBounds().height+5;
			image = new Image(getApp().display, logo.getBounds().width , logo.getBounds().height+100 );
		} else {
			hpos=170;
			image = new Image(getApp().display, 455, 250);
		}

		GCWrapper gc = new GCWrapper(image);
    gc.setBackground(getApp().display.getSystemColor(SWT.COLOR_WHITE));
    gc.fillRectangle(image.getBounds());
    gc.setForeground(getApp().display.getSystemColor(SWT.COLOR_BLACK));
 
		String sText = getApp().subname+"\nVersion: "+ConnectionManager.version+" - Build "+ConnectionManager.build+"\n";
				    
		try {  gc.drawImage(getIcon(sLogo), 0, 0); } catch (Exception e){}
		
    gc.setFont(new Font(getApp().display,"Arial",11,SWT.BOLD));
    gc.drawText(getApp().name, 10, hpos);
    gc.setFont(new Font(getApp().display,"Arial",10,SWT.NORMAL));
    gc.drawText(sText, 10, hpos+30);
    gc.setFont(new Font(getApp().display,"Arial",9,SWT.NORMAL));
    gc.drawText(ConnectionManager.copyright, 10, hpos+70);   
    gc.dispose();

    Label label = new Label(splash, SWT.NONE);
    label.setImage(image);
    FormLayout layout = new FormLayout();
    splash.setLayout(layout);
    FormData labelData = new FormData();
    labelData.right = new FormAttachment(100, 0);
    labelData.bottom = new FormAttachment(100, 0);
    label.setLayoutData(labelData);

    splash.pack();
    Rectangle splashRect = splash.getBounds();
    Rectangle appRect =  getApp().shell.getBounds();
    Point corner = getApp().shell.getLocation();
    int x = corner.x+(appRect.width - splashRect.width) / 2;
    int y = corner.y+(appRect.height - splashRect.height) / 2;    
    splash.setLocation(x, y);    
    splash.open();   
    getApp().display.asyncExec(new Runnable() {
      public void run() {
        try {
          Thread.sleep(1000);
        } catch (Throwable e) {
        }
        splash.close();
        image.dispose();
      }
    });
    
    return splash;
	}
	
	/**
	 * Display an Error Message for a thread Exception
	 * 
	 * @param exception		the thread Exception
	 * @param sTitle      the message box title
	 */
	public static void errorMessage(Shell shell, Exception exception,String sTitle) {
		String sMessage = exception.getMessage(); 
		if (sMessage==null) { sMessage = "No message"; }
		int iline=sMessage.indexOf(", on line");
		if (iline>0) sMessage=sMessage.substring(0,iline);
		if (shell!=null) {
			MessageBox msg = new MessageBox(shell,SWT.APPLICATION_MODAL | SWT.ICON_ERROR | SWT.OK );
			msg.setText(sTitle);
			msg.setMessage(sMessage);
			msg.open();
		} else { System.out.println(sMessage); }
	}

	/**
	 * Display a Message
	 * 
	 * @param sMessage		the showed message
	 * @param sTitle      the message box title
	 * @param style       the message type ( see SWT MessageBox styles )
	 */
	public static void showMessage(Shell shell, String sMessage ,String sTitle, int style) {
		if (sMessage==null) { sMessage = "No message"; }
		int iline=sMessage.indexOf(", on line");
		if (iline>0) sMessage=sMessage.substring(0,iline);	
		if (shell!=null) {
			MessageBox msg = new MessageBox(shell,SWT.APPLICATION_MODAL | style | SWT.OK );
			msg.setText(sTitle);
			msg.setMessage(sMessage);
			msg.open();
		} else { System.out.println(sMessage); }
	}

	
	/**
	 * Open a File Selection Dialog for Image files
	 * 
	 * @param sTitle   the dialog title
	 * @param style    the dialog style (see SWT FileDialog styles)
	 */
	public static String getIconFileName(Shell shell , String sTitle, int style){
		FileDialog file = new FileDialog(shell, style);
		String ext[] = {"*.png","*.jpg","*.gif","*.*"};
		String des[] = {"PNG Image file","JPEG Image file","GIF Image file","All files"};
		file.setText(sTitle);
		file.setFilterExtensions(ext);
		file.setFilterNames(des);
		file.open();
		if (file != null && file.getFileName().length() > 1) {
			if (!Application.isRWT) {
				return file.getFilterPath().replace('\\', '/') + "/" + file.getFileName();
			} else {
				return file.getFileName().replace('\\', '/');
			}
		} else return null;
	}
	
	/**
	 * Add a dialog popup to the dialog-window registry
	 * 
	 * @param sName    the dialog window name
	 * @param dialog   the dialog
	 */
	public static void addDialog(String sName,Shell dialog) {
		getApp().dialogs.put(sName, dialog);
	}

	/**
	 * Retrieve a dialog popup from the dialog-window registry
	 * 
	 * @param sName    the dialog window name
	 */
	public static Shell getDialog(String sName) {
		Shell dialog = getApp().dialogs.get(sName);
		if (dialog!=null && dialog.isDisposed()) { 
			getApp().dialogs.remove(sName);
			return null; 
		} else { return dialog; }
	}
	
	
	/**
	 * Compose the object Class-Name-Revision string 
	 */
	public static String getClassNameRevision(ObjectObj object,HttpSession session) throws Exception{
		String sCNR = getTranslation(getApp().dictionary_section+"*",object.getClassName());
		return sCNR+" "+getNameRevision(object.getName(),object.getRevision());
	}
	
	/**
	 * Compose the Name-Revision string separated by name_revision_separator
	 */
	public static String getNameRevision(String sName,String sRevision) {
		sRevision=sRevision.trim();   
		if (sRevision.length()>0 && !sRevision.equals("-")) {
			return sName+getApp().name_revision_separator+sRevision;
		} else {
			return sName;
		}
	}

	/**
	 * Compose the link identification string as "Linktype From(Class/Name/Revision) -> To(Class/Name/Revision)"
	 * use the name_revision_separator between the object name and the revision
	 */
	public static String getLinkData(Link link,HttpSession session) throws Exception{
		String sCNRF = getTranslation(getApp().dictionary_section+"*",link.getFromClassName());
		if (link.getFromRevision().length()>0 && !link.getFromRevision().equals("-")) {
			sCNRF+=" "+link.getFromName()+getApp().name_revision_separator+link.getFromRevision();
		} else {
			sCNRF+=" "+link.getFromName();
		}
		String sCNRT = getTranslation(getApp().dictionary_section+"*",link.getToClassName());
		if (link.getToRevision().length()>0 && !link.getToRevision().equals("-")) {
			sCNRT+=" "+link.getToName()+getApp().name_revision_separator+link.getToRevision();
		} else {
			sCNRT+=" "+link.getToName();
		}
		String sType = getTranslation(getApp().dictionary_section+"*",link.getLinkType());
		return sType+" "+sCNRF+" -> "+sCNRT;
	}

	/**
	 * Translate a keyword searching in the Application Common dictionary-Section
	 */
	public static String getCommonTranslation(String key) {
		if (key!=null && key.length()>0) {
			try {
				return Dictionary.getKey(getApp().dictionary_common_section, key , getApp().framework);
			} catch (Exception ex) { return key; }
		} else { return key; }
	}

	/**
	 * Translate a keyword searching in the Application Menu dictionary-Section
	 */
	public static String getMenuTranslation(String key) {
		if (key!=null && key.length()>0) {
			try {
				return Dictionary.getKey(getApp().dictionary_menu_section, key , getApp().framework);
			} catch (Exception ex) { return key; }
		} else { return key; }

	}

	/**
	 * Translate a keyword searching in the Application dictionary-Section
	 */
	public static String getTranslation(String key) {
		if (key!=null && key.length()>0) {
			try {
				return Dictionary.getKey(getApp().dictionary_section, key , getApp().framework);
			} catch (Exception ex) { return key; }
		} else { return key; }

	}

	/**
	 * Translate a keyword searching in a specific dictionary-Section
	 */
	public static String getTranslation(String section,String key) {
		if (section!=null && section.length()>0 && key!=null && key.length()>0) {
			try {
				return Dictionary.getKey(section, key , getApp().framework);
			} catch (Exception ex) { return key; }
		} else { return key; }
	}
	
	/**
	 * Translate a keyword to a specific language, searching in a specific dictionary-Section
	 */
	public static String getTranslation(String language,String section,String key) {
		if (language!=null && language.length()>0 
				&& section!=null && section.length()>0 && key!=null && key.length()>0) {
			try {
				return Dictionary.getKey(language,section, key , getApp().framework);
			} catch (Exception ex) { return key; }
		} else { return key; }
	}
	
	/**
	 * Generate the application main Menu
	 * 
	 * @param menuName   the name of {@link Menu}
	 */
	public org.eclipse.swt.widgets.Menu createMenu(String menuName) {
		shell.setFont(globalfont);
		com.oberon.ooql.sdk.Menu mainmenu = null;
		// Load application parameters
		try {
			mainmenu = Menu.open(menuName,framework,null);			
			try {
				appIcon=getApp().getImage("Menu", menuName, 0, 0);
				shell.setImage(appIcon); 
			} catch (Exception ex) {}
			Vector<Feature> vFeatures = mainmenu.getFeatures(framework);
			if (vFeatures!=null) {
				for (int i=0;i<vFeatures.size();i++) {
					Feature feature = vFeatures.elementAt(i);
					if (feature.getTo().length()==0) {
						String sFeature = feature.getName();  
						if ( sFeature.equals(TAGS.APP_dictionary_menu_section)) dictionary_menu_section=feature.getValue();
						if ( sFeature.equals(TAGS.APP_dictionary_common_section)) dictionary_common_section=feature.getValue();
						if ( sFeature.equals(TAGS.APP_dictionary_section)) dictionary_section=feature.getValue();		
						if ( sFeature.equals(TAGS.APP_dictionary_default_language)) dictionary_default_language=feature.getValue();
						if ( sFeature.equals(TAGS.APP_show_object_class)) show_object_class=feature.getValue().equals("true");
						if ( sFeature.equals(TAGS.APP_show_object_namerev)) show_object_namerev=feature.getValue().equals("true");
						if ( sFeature.equals(TAGS.APP_use_search_patterns)) use_search_patterns=feature.getValue().equals("true");
						if ( sFeature.equals(TAGS.APP_number_separator_symbols)) number_separator_symbols=feature.getValue();
						if ( sFeature.equals(TAGS.APP_name_revision_separator)) name_revision_separator=feature.getValue();
						if ( sFeature.equals(TAGS.APP_search_all_label)) search_all_label=feature.getValue();
					}
				}
			}
			// Combines the dictionary sections for portfolio applications 
			if (mainmenu.isPortfolio()) {
				Vector<String> vMenuSections = StringUtils.StringTokensToVector(dictionary_menu_section, ",");
				Vector<String> vCommonSections = StringUtils.StringTokensToVector(dictionary_common_section, ",");
				Vector<String> vAppSections = StringUtils.StringTokensToVector(dictionary_section, ",");
				Vector<String> vApplications = mainmenu.getOwnSubMenus();
				for (int i=0;i<vApplications.size();i++) {
					String sApplicationName = vApplications.elementAt(i);
					try {
						Feature feature = Feature.open(TAGS.APP_dictionary_menu_section, "Menu",sApplicationName,"","", framework, null);
						if (vMenuSections.indexOf(feature.getValue())<0) {
							vMenuSections.add(feature.getValue());
						}
					} catch (Exception ex) {}
					try {
						Feature feature = Feature.open(TAGS.APP_dictionary_common_section, "Menu",sApplicationName,"","", framework, null);
						if (vCommonSections.indexOf(feature.getValue())<0) {
							vCommonSections.add(feature.getValue());
						}
					} catch (Exception ex) {}
					try {
						Feature feature = Feature.open(TAGS.APP_dictionary_section, "Menu",sApplicationName,"","", framework, null);
						if (vAppSections.indexOf(feature.getValue())<0) {
							vAppSections.add(feature.getValue());
						}
					} catch (Exception ex) {}					
				}			
				dictionary_menu_section=StringUtils.vectorToString(vMenuSections,",");
				dictionary_common_section=StringUtils.vectorToString(vCommonSections,",");
				dictionary_section=StringUtils.vectorToString(vAppSections,",");
			}
		} catch (Exception ex) {}
		
		org.eclipse.swt.widgets.Menu appmenu = new org.eclipse.swt.widgets.Menu(shell,SWT.BAR);
		setFramework(session,framework);
		if (framework.getLanguage().length()==0) {
			framework.setLanguage(dictionary_default_language);
		}
		session.setAttribute(Arg.Language , framework.getLanguage() );
		framework.emptyEnv();
		if (mainmenu!=null) {
			try {				
				framework.setEnv(TAGS.APP_dictionary_menu_section, dictionary_menu_section);
				createMenu( appmenu, mainmenu.getActions(new Vector<Object>(0), framework) , (ApplicationSession)session ,0 );
			} catch (Exception ex) {}
		}
		return appmenu;
	}

	// recurse the createMenu for sub-Menus
	private void createMenu(org.eclipse.swt.widgets.Menu parentmenu, Vector<com.oberon.ooql.sdk.MenuItem> vActions , ApplicationSession session, int level)  {
		try {
			int iSize = vActions.size();
			for (int i=0;i<iSize;i++) {
				com.oberon.ooql.sdk.MenuItem action = vActions.elementAt(i);	
				if (action.getLevel()>level) continue;
				Vector<com.oberon.ooql.sdk.MenuItem> subitems = action.getSubItems();
				if ( subitems==null || subitems.size()==0 ) {	// Is a command	  
					MenuItem item = new MenuItem(parentmenu, SWT.PUSH);
					item.setText(action.getTranslatedLabel());
					if (action.getIcon()!=null) {
						try { item.setImage( convertImage(action.getIcon(),0,0) );} catch (Exception e){}
					}  
					item.addSelectionListener(new ToolBarHandlerApp(action.getHRef()));					
				} else {
					MenuItem item = new MenuItem(parentmenu, SWT.CASCADE);
					item.setText(action.getTranslatedLabel());
					if (action.getIcon()!=null) {
						try { item.setImage( convertImage(action.getIcon(),0,0) );} catch (Exception e){}
					} 
					org.eclipse.swt.widgets.Menu subMenu= new org.eclipse.swt.widgets.Menu(shell,SWT.DROP_DOWN);
					item.setMenu(subMenu);
					createMenu(subMenu,subitems,session,level+1);
				}	
			}
		} catch (Exception e) { log(e.getMessage() );}
	}
		
	private class ToolBarHandlerApp implements SelectionListener {	
		String sCommand;
		ToolBarHandlerApp(String sCommand){
			this.sCommand=sCommand;
		};
		public void widgetDefaultSelected(SelectionEvent e) {};
		public void widgetSelected(SelectionEvent e) {
			handleRequest(new ApplicationRequest(sCommand,(ApplicationSession)session));
		}
	};

	/**
	 * Set a custom property to SWT widget 
	 * 
	 * @param widget   the SWT widget
	 * @param tag      the property name
	 * @param value    the property value
	 */
	public static void setData(Widget widget,String tag,Object value) {
		widget.setData(tag,value);
	}
	
	/**
	 * Get a custom property value from SWT widget 
	 * @param widget   the SWT widget
	 * @param tag      the property name
	 */
	public static Object getData(Widget widget,String tag) {
		return widget.getData(tag); 
	}
	
	/**
	 * Add a file name to the download list
	 * 
	 * @since 4.2.2
	 */
	public static void addDownloadFile(String sFileName) {
		if (!StringUtils.isBlank(sFileName)) getApp().vDownloadFiles.add(sFileName);
	}
	
	/**
	 * Return the file download list
	 * 
	 * @since 4.2.2
	 */
	public static Vector<String> getDownloadFiles() {
		return new Vector<String>(getApp().vDownloadFiles);
	}

	/**
	 * Open new PopUp window (dialog)
	 *  
	 * @param parent       the parent shell
	 * @param request      Application request with target url
	 * @param windowName   the dialog window name 
	 * @param style        the HTML window style (width,height,resizable,scrollable...)
	 * 
	 * @return  the dialog Shell
	 */
	public static Shell windowOpen(Composite parent,ApplicationRequest request,String windowName,String style) {  	 
		Shell popDialog=getDialog(windowName);
		if (popDialog==null) {
 			int iTitle  = (style.indexOf("titlebar=no")>=0  || style.indexOf("titlebar=0")>=0)?SWT.ON_TOP:0;
			int iResize = (style.indexOf("resizable=no")>=0 || style.indexOf("resizable=0")>=0)?0:SWT.RESIZE;
			popDialog = new Shell (parent.getShell(), SWT.DIALOG_TRIM | iResize | iTitle);	
			addDialog(windowName, popDialog);
			popDialog.setSize(400,300);
			popDialog.setBackgroundMode(SWT.INHERIT_DEFAULT); 
			popDialog.setLayout(new FillLayout());
			try { popDialog.setImage(getApp().appIcon); } catch (Exception ex) {}
			setData(popDialog,TAGS.WINDOW,popDialog);
			setData(popDialog,TAGS.OPENER,getWindow(parent));
		}
		try {
			StringTokenizer sT = new StringTokenizer(style,",");
			while (sT.hasMoreTokens()) {
				String param = sT.nextToken().trim();
				StringTokenizer sTV=new StringTokenizer(param,"=");
				param=sTV.nextToken().trim();
				if (sTV.hasMoreTokens()) {
					String value = sTV.nextToken(); 
					if (param.equals("width")) {
						popDialog.setSize(Integer.parseInt(value)+20, popDialog.getSize().y);
					} else  if (param.equals("height")) {
						popDialog.setSize(popDialog.getSize().x,Integer.parseInt(value)+45);
					} else  if (param.equals("left")) {
						popDialog.setLocation(Integer.parseInt(value), popDialog.getLocation().y);
					} else  if (param.equals("top")) {
						popDialog.setLocation(popDialog.getLocation().x,Integer.parseInt(value));
					} else  if (param.equals("titlebar")) {
						setData(popDialog,TAGS.pagetitle,!(value.equals("no") || value.equals("0")));
					} else  if (param.equals("scrollbars")) {
						setData(popDialog,TAGS.scrollbars,!(value.equals("no") || value.equals("0")));
					} 
				} 		   
			}
			/*
			 SUPPORTED in SWT	
			   titlebar=yes|no|1|0	Whether or not to display the title bar. Ignored unless the calling application is an HTML Application or a trusted dialog box. Default is yes
			   resizable=yes|no|1|0	Whether or not to display window is resizable. Default is yes
			   scrollbars=yes|no|1|0	Whether or not to display scroll bars. Default is yes		   
				   
			   NOT SUPPORTED
			   toolbar=yes|no|1|0	Whether or not to display the browser toolbar. Default is yes
			   location=yes|no|1|0	Whether or not to display the address field. Default is yes       
			   status=yes|no|1|0	Whether or not to add a status bar. Default is yes
			*/ 
			
			setData(popDialog,TAGS.REQUEST,request);
			setData(popDialog,TAGS.PARAMS,style);
			request.setReferer(parent);
			request.setTarget(windowName);
			getApp().handleRequest(request);
		} catch (Exception ex) {}		
		return popDialog;
	}
	
	public static Widget getWindow(Control view) {
		while ( view!=null && getData(view,TAGS.WINDOW)==null ) {
			view = view.getParent();
		}
		if (view.getParent() instanceof CTabFolder) { return (Widget)view.getData(TAGS.WINDOW); }
		return view;
	}
	
	/**
	 * Show a message in the application Log panel or in the status bar 
	 */
	public abstract void log(String message);

	/**
	 * Open the Framework dialog to login into OBEROn
	 */
	public abstract void setFramework();
	
	
	/**
	 * Handle an Application request
	 *
	 * @return true when the request page is recognized as valid page
	 */
	public abstract boolean handleRequest(ApplicationRequest request);
	
	
	/**
	 * Convert the language code from the Application format to standard ISO code
	 */
	public abstract String translateOberonLanguageToISO639Language(String language);
			
}

