/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2008  A.Brochard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License only.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package net.pms.newgui;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;	// regzamod add
import java.awt.event.KeyListener;	// regzamod add
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.StringTokenizer;	//regzamod
import java.util.List;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;	//regzamod add
import java.text.DecimalFormat;	//regzamod add

import net.pms.PMS;		//regzamod
import net.pms.Messages;
import net.pms.logging.LoggingConfigFileLoader;
import net.pms.io.BufferedOutputFileImpl;	// regzamod add
import net.pms.network.RequestV2;	// regzamod add
import net.pms.dlna.DLNAResource;	// regzamod
import net.pms.configuration.PmsConfiguration;	// regzamod add
import net.pms.configuration.RendererConfiguration;
import net.pms.dlna.rz_SessionCtl;	//regzamod
import net.pms.dlna.rz_SessionInfo;	//regzamod

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class TracesTab {
	private static final Logger logger = LoggerFactory.getLogger(TracesTab.class);
	private DecimalFormat formatter = new DecimalFormat("#,###");
	
	private final PmsConfiguration configuration;	//regzamod add
	private JTextField f_loglevel;	//regzamod add
	private int cur_sess_no=0;

	TracesTab(PmsConfiguration configuration) {	//regzamod add
		this.configuration = configuration;
	}

	class PopupTriggerMouseListener extends MouseAdapter {
		private JPopupMenu popup;
		private JComponent component;

		public PopupTriggerMouseListener(JPopupMenu popup, JComponent component) {
			this.popup = popup;
			this.component = component;
		}

		// Some systems trigger popup on mouse press, others on mouse release, we want to cater for both
		private void showMenuIfPopupTrigger(MouseEvent e) {
			if (e.isPopupTrigger()) {
				popup.show(component, e.getX() + 3, e.getY() + 3);
			}
		}

		// According to the javadocs on isPopupTrigger, checking for popup trigger on mousePressed and mouseReleased 
		// Should be all that is required

		public void mousePressed(MouseEvent e) {
			showMenuIfPopupTrigger(e);
		}

		public void mouseReleased(MouseEvent e) {
			showMenuIfPopupTrigger(e);
		}
	}
	private JTextArea jList;

	public JTextArea getList() {
		return jList;
	}

	public JComponent build() {

		//------------------------------------------------------------------------------------------------
		// FormLayout String Expression Syntax ,regzamod
		//------------------------------------------------------------------------------------------------
		/*
		columnSpec            ::=   [columnAlignment:] size [:resizeBehavior] 
		rowSpec               ::=   [rowAlignment :] size [:resizeBehavior] 
		columnAlignment       ::=   LEFT | CENTER | RIGHT | FILL | L | C | R | F 
		rowAlignment          ::=   TOP | CENTER | BOTTOM | FILL | T | C | B | F 
		size                  ::=   constantSize | componentSize | boundedSize 
		componentSize         ::=   MIN | PREF | DEFAULT | M | P | D 
		constantSize          ::=   <integer>integerUnit | <double>doubleUnit 
		integerUnit           ::=   PX | PT | DLU 
		doubleUnit            ::=   IN | MM | CM 
		boundedSize           ::=   MIN(constantSize;componentSize)| MAX(constantSize;componentSize) 
		resizeBehavior        ::=   NONE | GROW | GROW(<double>) | G(<double>)		
		*/
		//------------------------------------------------------------------------------------------------
		// dlu: may be 1point used in Fontsize
		
		/* Origin
		FormLayout layout = new FormLayout(
			"left:pref, 10:grow",
			"fill:10:grow, p");
		*/
		FormLayout layout = new FormLayout(
			//"left:pref, L:40dlu, L:p, 10:grow",	// 4 columns (each separated by comma",")
			"left:pref, L:80dlu, L:p, 10:grow",	// 4 columns (each separated by comma",")
			"fill:10:grow, p");				// 2 rows (each separated by comma",")
		
		PanelBuilder builder = new PanelBuilder(layout);
		//  builder.setBorder(Borders.DLU14_BORDER);
		builder.setOpaque(true);

		CellConstraints cc = new CellConstraints();

		//create trace text box
		jList = new JTextArea();
		jList.setEditable(false);
		jList.setBackground(Color.WHITE);
		//jList.setFont(new Font("Arial", Font.PLAIN, 12));
		final JPopupMenu popup = new JPopupMenu();
		JMenuItem defaultItem = new JMenuItem(Messages.getString("TracesTab.3"));

		defaultItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				jList.setText("");
			}
		});

		popup.add(defaultItem);
		jList.addMouseListener(
			new PopupTriggerMouseListener(
			popup,
			jList));

		JScrollPane pane = new JScrollPane(jList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		builder.add(pane, cc.xyw(1, 1, 4));

		//---- regzamod add start -------------------------------
		// for Logging Level Setting
		
		builder.addLabel("  Log Level  ", cc.xy(1, 2));
		
		String level_d=configuration.getRZ_LogbackLevel_disp();
		String level_f=configuration.getRZ_LogbackLevel_file();
		if(level_d==null) {
			level_d="Unknown";
		}
		if(level_f==null) {
			level_f="Unknown";
		}
		f_loglevel = new JTextField(level_d+","+level_f,30);  // 30: text_length --> no effect
		
		f_loglevel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String line=f_loglevel.getText();
				StringTokenizer st = new StringTokenizer(line, ",");
				//if(logger==null) logger = LoggerFactory.getLogger(TracesTab.class);
				if(!st.hasMoreTokens()) {
					f_loglevel.setText(configuration.getRZ_LogbackLevel_disp()+","+configuration.getRZ_LogbackLevel_file());
				}
				else {
					String str=null;
					for(int i=0;i<4 && st.hasMoreTokens();i++) {
						String token = st.nextToken().trim();
						if(i==0) {	// slf4j LogLevel or else commands
							configuration.setRZ_LogbackLevel_disp(token);
						}
						else if(i==1) {
							configuration.setRZ_LogbackLevel_file(token);
						}
						else if(i==2) {	// set rz_debug value or else
							if(token.equals("help")||token.equals("h")||token.equals("?")) {
								logger.info("===== Commands Format ======================");
								logger.info("{LogLevel_disp}, {LogLevel_file} [,OptionCommand]");
								logger.info("===== OptionCommands =======================");
								logger.info("dlna       : disp current dlna profile");
								logger.info("dlna={str} : disp dlna that's name contains the str under cur_sess & cur_folder");
								logger.info("sess       : disp current session list");
								logger.info("sess={num} : set cur_sess_num");
								logger.info("rep={n}    : reply HTTP Error (type=n) when streaming error (n=0:Noop)");
								logger.info("test={n}   : set test_mode (rz_test)");
								logger.info("debug={n}  : dump debug info");
								logger.info("dump={n}   : dump stream(n=1),dump thumnbnail(n=2)");
								logger.info("trace={n}  : dump performance trace,=1:file,=2:disp");
								logger.info("uh={0|1|2} : dump user header (=0:Off,=1:On,=2:On+verbose)");
								logger.info("gc         : force garbage collection");
								logger.info("pm         : purge unused memories");
								logger.info("============================================");
							}
							if(token.startsWith("sess=")) {
								str=token.substring("sess=".length()).trim();
								if(str.length()<=0) continue;
								cur_sess_no=Integer.parseInt(str);
								logger.info("----> set cur_sess_num="+cur_sess_no);
							}
							if(token.startsWith("dlna=")) {
								str=token.substring("dlna=".length()).trim();
								if(str.length()<0) continue;
								String sch=str;
								logger.info("----> exec search dlna name contains '"+sch+"'");
								
								List <rz_SessionInfo> sessList=PMS.rz_SessCtl.getSessAll(); 
								if(cur_sess_no<0 || cur_sess_no>=sessList.size()) {
									logger.info("----> sess_num is over range");
									continue;
								}
								rz_SessionInfo ss=sessList.get(cur_sess_no);
								DLNAResource f=null;
								RendererConfiguration r=null;
								if(ss!=null && ss.CurrentFolderID!=null) {
									r=ss.renderer;
									f=PMS.get().getRootFolder(r).search(ss.CurrentFolderID,1,r,false,null);
								}
								int cnt=0;
								boolean all=false;
								if(sch.length()==0) all=true;
								if(f!=null && f.isFolder()) {
									for (DLNAResource d : f.getChildren()) {
										if(d.getDisplayName(r).contains(sch)||all) {
											dump_dlna(d,ss,cur_sess_no, r);
											cnt++;
										}
									}
								}
								if(cnt==0) logger.info("----> matching DLNA not found");
							}
							else if(token.equals("dlna")) {
								List <rz_SessionInfo> sessList=PMS.rz_SessCtl.getSessAll(); 
								int sno=0;
								int dcnt=0;
								for(rz_SessionInfo ss : sessList) {
									if(ss.sa==null) {
										continue;
									}
									DLNAResource d=ss.CurrentDLNA;
									RendererConfiguration r=ss.renderer;
									if(d!=null) {
										dump_dlna(d,ss,sno,r);
										dcnt++;
									}
									sno++;
								}
								if(dcnt<=0) {
									logger.info("----> Current DLNA not exists");
								}
							}
							else if(token.equals("sess")) {
								List <rz_SessionInfo> sessList=PMS.rz_SessCtl.getSessAll(); 
								int cnt=0;
								logger.info("=========== Session List ========================================");
								for(rz_SessionInfo s : sessList) {
									if(s.sa!=null) {
										//logger.info("Sess No"+i+", SockAddress="+a.sa.getAddress());
										int lock=s.renderer==null?0:s.renderer.rootFolder==null?0:s.renderer.rootFolder.rz_MenuLock;
										logger.info("Sess No"+cnt+", Lock="+lock+", "+s.toString());
										cnt++;
									}
								}
								logger.info("==============================================================");
							}
							else if(token.equals("gc")) {
								logger.info("exec garbage collection start, buffer_cnt="+BufferedOutputFileImpl.alloc_cnt);
								System.gc();
								logger.info("exec garbage collection end");
							}
							else if(token.startsWith("dump=")) {
								str=token.substring("dump=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_stream_dump=val;
								logger.info("dump="+val);
							}
							else if(token.startsWith("rep=")) {
								str=token.substring("rep=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_reply_error=val;
								logger.info("error_reply="+val);
							}
							else if(token.startsWith("uh=")) {
								str=token.substring("uh=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_dump_ua_header=val;
								logger.info("rz_dump_ua_header="+PMS.rz_dump_ua_header);
							}
							else if(token.startsWith("test=")) {
								str=token.substring("test=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_test=val;
								logger.info("rz_test="+val);
							}
							else if(token.startsWith("trace=")) {
								str=token.substring("trace=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_trace=val;
								logger.info("rz_trace="+val);
							}
							else if(token.equals("pm")) {
								//str=token.substring("mp".length()).trim();
								//if(str.length()<=0) continue;
								//int val=Integer.parseInt(str);
								logger.info("==== exec PurgeMemory start");
								PMS.PurgeMemory();
								logger.info("==== exec PurgeMemory end");
							}
							else if(token.startsWith("debug=")) {
								str=token.substring("debug=".length()).trim();
								if(str.length()<=0) continue;
								int val=Integer.parseInt(str);
								PMS.rz_debug=val;
								logger.info("rz_debug="+val);
							}
							else if(token.matches("[-]?[0-9]*")) {
								PMS.rz_debug=Integer.parseInt(token);
							}
						}
					}
				}
				long m_max=Runtime.getRuntime().maxMemory() /1024;
				long m_total=Runtime.getRuntime().totalMemory() /1024;
				long m_free=Runtime.getRuntime().freeMemory() /1024;
				
				logger.info("LogLevel_disp="+configuration.getRZ_LogbackLevel_disp()
					+", LogLevel_file="+configuration.getRZ_LogbackLevel_file()
					+", rz_debug=" + PMS.rz_debug + ", rz_trace=" +PMS.rz_trace+ ", rz_test=" +PMS.rz_test
					+", rz_stream_dump=" +PMS.rz_stream_dump+", error_reply= "+PMS.rz_reply_error);
			  	
				logger.info("Remaining buffer alloc_cnt="+BufferedOutputFileImpl.alloc_cnt
			  		+", alloc_size="+BufferedOutputFileImpl.alloc_size
			  		+", Java maxMem="+formatter.format(m_max)
			  		+", totalMem="+formatter.format(m_total)
			  		+", usedMem="+formatter.format(m_total-m_free)
			  		+", freeMem="+formatter.format(m_free)+" KB");
				logger.info("===> Input ',h' for Help and More info");
			}
		});

		/* KeyListner gets every keyStrokes!!
		f_loglevel.addKeyListener(new KeyListener() {
			@Override
			public void keyPressed(KeyEvent e) {
			}

			@Override
			public void keyTyped(KeyEvent e) {
			}

			@Override
			public void keyReleased(KeyEvent e) {
				configuration.setRZ_LogbackLevel(f_loglevel.getText());
			}
		});
		*/
		
		// CellConstraints is defined using logical grid
		// grid is relative positions&range of Components: 
		// example: cc.xywh(3, 5, 1, 1, CellConstraints.RIGHT, CellConstraints.BOTTOM);
		//builder.add(f_loglevel, cc.xy(2, 2));
		builder.add(f_loglevel, cc.xy(2, 2));
		
		builder.addLabel(" (ALL|TRACE|DEBUG|INFO|WARN|ERROR|FATAL|OFF) ", cc.xy(3, 2));

		//---- regzamod add end ---------------------------

		// Add buttons opening log files
		JPanel pLogFileButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		HashMap<String, String> logFiles = LoggingConfigFileLoader.getLogFilePaths();
		for (String loggerName : logFiles.keySet()) {
			JButton b = new JButton(loggerName);
			b.setToolTipText(logFiles.get(loggerName));
			b.addMouseListener(new MouseAdapter() {
				@Override
				public void mouseClicked(MouseEvent e) {
					File logFile = new File(((JButton) e.getSource()).getToolTipText());
					try {
						java.awt.Desktop.getDesktop().open(logFile);
					} catch (IOException e1) {
						logger.error(String.format("Failed to open file %s in default editor", logFile), e1);
					}
				}
			});
			pLogFileButtons.add(b);
		}
		builder.add(pLogFileButtons, cc.xy(4, 2));

		return builder.getPanel();
	}
	
	private void dump_dlna(DLNAResource d, rz_SessionInfo ss, int scnt, RendererConfiguration r) {
		if(d==null) return;
		
		//DLNAResource d=RequestV2.CurrentDLNA;
		logger.info("========= Current DLNA profile ================================");
		logger.info("-- Session No."+scnt+", Renderer Name = "+(r==null?"null":r.toString()));
		if(ss!=null && ss.CurrentFolderID!=null) {
			DLNAResource f=PMS.get().getRootFolder(r).search(ss.CurrentFolderID,1,r,false,null);
			if(f!=null && f.isFolder()) logger.info("-- Current Folder Name= "+f.getName()+", ID = "+ss.CurrentFolderID);
		}
		logger.info("-- DLNA Name = "+d.getName()+", Path="+d.getSrcPath());
		logger.info("-- DLNA DlnaName="+d.getDlnaName(r)+", Ext="+d.getExt());
		
		if(r!=null) {
			logger.info("-- DLNA Prof = "+d.toString(r));
		}
		else {
			logger.info("-- DLNA Prof = "+d.toString());
		}
		
		//---- TEST Start
		logger.info("-- MimeType org ="+d.gMimeType_org+", ren="+d.gMimeType_ren+", cur="+d.gMimeType_cur);
		logger.info("-- DlnaContentFeatures ="+d.getDlnaContentFeatures());
		logger.info("-- DisplayName ="+d.getDisplayName()+", rz_MetaTransType[0]="+d.rz_MetaTransType[0]);
		logger.info("-- SerialId ="+d.getSerialId()+", MetaSortType="+d.rz_MetaSortType+", sort_immune="+d.sort_immune+", doSort="+d.doSort);
		//---- TEST End
		
		logger.info("-- Mediainfo = "+d.getMedia());
		logger.info("-- Player = "+d.getPlayer());
		if(d.bitrateForTimeSeek>0) {
			String s=String.format("-- Estimated BitrateForTimeSeek=%.3f( Mbps)",(d.bitrateForTimeSeek/1024.0));
			logger.info(s);
		}
		logger.info("==============================================================");
		//logger.info("-- MimeType     = "+d.mimeType());
	}

}
