function gui = t1shell( debug )
%
% T1-Shell: user interface and wrapper around the MATLAB interactive script
% functions for the lecture 'Technische Informatik 1' at Univ. Hamburg, 
% Dept. Computer Science.
%
% While T1-Shell can be used via the Matlab workspace without problems,
% its main motivation is to provide a 'workspace light' from a precompiled
% Matlab application. This way, our students don't need a full Matlab
% license to run our functions. However, as t1shell allows to specify
% function parameters, explorative learning is fully possible.
%
% In order to comply with MathWorks, Inc. licensing for Matlab Compiler,
% we keep and manage a list of 'allowed' functions. Only functions from
% this list can be called via t1shell.
%
% (C) 2002-2005, K.v.d.Heide (v.d.heide@on-line.de) and
% (C) 2004-2005, N.Hendrich  (hendrich@informatik.uni-hamburg.de)
%
% The network functions use the tcp_udp_ip toolbox,
% (C) 2002, Peter Rydesäter.
% Please see the file 'license.txt' for the special licensing (GPL+exception)
% for the tcp_udp_ip toolbox.
%
%
  disp( 'T1-Shell initializing, please wait...' );
  gui = {};

  % try to load the precompiled list of functions, their arguments,
  % and their ordering within the script. There is no use to continue
  % without this function list...
  %
  try 
    gui = load( 't1shell.mat' );
    % disp( size(tmp) );
    % gui.info = tmp;
  catch
    disp( 'Datei t1shell.mat konnte nicht geladen werden.' );
    disp( lasterr );
    errordlg( 'Konfigurationsdatei t1shell.mat fehlt!', ...
              'T1-Shell: Abbruch' );
    return;
  end

  if (nargin < 1) gui.debug = 0; 
  else            gui.debug = debug;
  end

  % create the alphabetically sorted list of functions and the
  % chapter-based list of functions
  n_functions  = length( gui.info.fnames );

  flist_abc = {};
  flist_123 = {};
  for i=1:n_functions
    s = [ char(gui.info.fnames(i)) char(gui.info.fargs(i)) ]; 
    % doesn't work either: s = num2str( rand );
    flist_abc(i) = { s };
    flist_123(i) = { [ char(gui.info.scripts(i)) ...
                     char(gui.info.fnames(i)) ...
                     char(gui.info.fargs(i)) ] };
  end
  flist_abc = sort( flist_abc );

  gui.flist_abc = flist_abc;
  gui.flist_123 = flist_123;


  % create the main window
  %
  gui.figure = figure( 'Name', 'T1-Shell: Technische Informatik I Demos', ...
                       'NumberTitle', 'off', ...
                       'MenuBar', 'none', ...
                       'Units', 'pixels', ...
                       'Position', [50 50 750 600], ...
                       'WindowStyle', 'normal' );
  bgcolor    = get( gui.figure, 'Color' );

  % initialize the menus
  %
  m_file = cmenu( 'File' );
  m_open = cmi( m_file, 'Open', 'O', @doOpen, 0 );
  m_save = cmi( m_file, 'Save', 'S', @doSave, 0 );
  m_saveas = cmi( m_file, 'Save as...', '', @doSaveAs, 0 );
  m_exit = cmi( m_file, 'Quit', 'Q', @doQuit' );
  cmseparator( m_exit );
  
  m_edit = cmenu( 'Edit' );
  m_copy = cmi( m_edit, 'Copy', 'C', @doCopy, 0 );
  m_cut  = cmi( m_edit, 'Cut',  'X', @doCut,  0 );
  m_paste= cmi( m_edit, 'Paste','V', @doPaste,0 );
  m_start= cmi( m_edit, 'Start Server (port 8888)', '', @doStartServer, 0 );
  cmseparator( m_start );
  m_stop = cmi( m_edit, 'Stop Server', '', @doStopServer, 0  );
  m_close_all = cmi( m_edit, 'Close all windows', 'B', @doCloseAll );
  cmseparator( m_close_all );
  m_demo = cmi( m_edit, 'Demo', 'U', {@doDemo;gui} );
  cmseparator( m_demo );
  
  m_help = cmenu( 'Help' );
  m_about = cmi( m_help, 'About', '', @doShowAbout );
  % cmseparator( m_about );
  m_usage = cmi( m_help, 'Usage', '', @doShowUsage );
  m_license = cmi( m_help, 'License', '', @doShowLicense );
  % m_fhelp = cmi( m_help, 'Function Info', '', 'doShowFHelp' );
  % cmseparator( m_fhelp );


  % create main window GUI controls: 
  %
  gui.banner = uicontrol( 'style', 'text', 'units', 'pixels', ...
                        'position', [50, 550, 450, 35], ...
                        'BackgroundColor', bgcolor, ...
                        'HorizontalAlignment', 'left', ...
                        'FontSize', 20, ...
                        'string', 'T1: Interaktive Demos' );


  %gui.flb    = uicontrol( 'style', 'text', 'units', 'pixels', ...
  %                      'position', [50, 50, 300, 25], ...
  %                      'HorizontalAlignment', 'left', ...
  %                      'BackgroundColor', bgcolor, ...
  %                      'string', 'Funktion und Argumente auswählen:' ...
  %                    );
  

  x =  50;
  y = 510;
  gui.buttongroup = uibuttongroup( ...
                                'visible','off', ...
                                'Position',[0 0 0.001 0.001] );

  gui.sort_label  = uicontrol( 'style', 'text', ...
                               'position', [x, y-7, 80, 30], ...
                               'BackgroundColor', bgcolor, ...
                               'HorizontalAlignment', 'left', ...
                               'string', 'Sortierung:' );
  gui.abc_button  = uicontrol( 'style','radiobutton', ...
                               'String','alphabetisch ',...
                               'position', [x+80, y, 120, 30], ...
                               'parent', gui.buttongroup, ...
                               'HandleVisibility','off' );
  gui.t123_button = uicontrol( 'style','radiobutton', ...
                               'String','nach Kapiteln',...
                               'position', [x+80+125, y, 120, 30], ...
                               'parent', gui.buttongroup, ...
                               'HandleVisibility','off' );
  set( gui.buttongroup, 'SelectedObject', gui.t123_button );
  set( gui.buttongroup, 'Visible', 'on' );

  % list-box with sorted function names
  %
  gui.flist  = uicontrol( 'style', 'list', ...
                          'units',    'pixels', ...
                          'position', [50, 200, 650, 300], ...
                          'FontSize', 12, ...
                          'FontName', 'FixedWidth', ...
                          'string', gui.flist_123 ...
                       );

  % command line textfield
  %
  x = 50; y = 160; 
  gui.cmd_label =   uicontrol( 'style', 'text', ...
                               'position', [x, y, 150, 30], ...
                               'BackgroundColor', bgcolor, ...
                               'HorizontalAlignment', 'left', ...
                               'string', 'Kommandozeile:' );
  y = 140; w = 650; h = 35;
  gui.cmdtf  = uicontrol( 'style', 'edit', ...
                          'units',    'pixels', ...
                          'position', [x, y, w, h], ...
                          'FontSize', 12, ...
                          'FontName', 'FixedWidth', ...
                          'HorizontalAlignment', 'left', ...
                          'string', '' ...
                         );

  % run and info buttons
  %
  y = 100;
  gui.run_button  =  uicontrol( 'style', 'pushbutton', ...
                                'units', 'pixels', ...
                                'position', [x, y, 200, 30], ...
                                'string', 'Funktion ausführen' ...
                              );
  gui.info_button =  uicontrol( 'style', 'pushbutton', ...
                                'units', 'pixels', ...
                                'position', [x+250, y, 200, 30], ...
                                'string', 'Info zur Funktion' ...
                              );


  % NOTE: somehow, the stupid id*ots at Mathworks manage that callback
  % functions reference 'incomplete' structures - in this case an incomplete
  % gui structure, unless we wait to register the callbacks after all
  % other GUI work has been finished. Crazy...
  % 
  set( gui.buttongroup, 'SelectionChangeFcn', {@sort_flist_fcn;gui} );
  set( gui.run_button,  'Callback', {@run_button_fcn;gui} );
  set( gui.info_button, 'Callback', {@show_function_info;gui} );
  set( gui.flist,       'Callback', {@flist_callback_fcn;gui} );
  
end




function sort_flist_fcn( src, event, gui )
  if (gui.debug == 1) disp( 'sort_flist_fcn: ' ); end

  alphabetical = get( gui.abc_button, 'Value' );

  % now update function list-box. We need the extra check, because this
  % function might be called before gui.flist is created...
  try 
    if (alphabetical) 
      set( gui.flist, 'string', gui.flist_abc, 'fontname', 'FixedWidth' );
    else

      set( gui.flist, 'string', gui.flist_123, 'fontname', 'FixedWidth' );
    end
  catch
    disp( lasterr ); 
  end
end


function flist_callback_fcn( src, event, gui )
  if (gui.debug==1) disp( 'flist_callback_fcn...' ); end;

  single_or_double = get( gui.figure, 'SelectionType' );
  if (strfind(single_or_double,'open'))
    double_click = 1;
  else 
    double_click = 0;
  end

  try
    index = get( gui.flist, 'Value' );
    alphabetical = get( gui.abc_button, 'Value' );
    if (alphabetical) 
      command = gui.flist_abc{index};
    else
      % in 'script sorted' mode, we must strip-off the first characters,
      % (namely the script index) from the actual command.
      trim    = length( char(gui.info.scripts(index)) )+1;
      command = gui.flist_123{index}(trim:end);
    end

    set( gui.cmdtf, 'string', command );

    if (double_click)
      execute_command( command );
    end

  catch
    disp( lasterr );
  end
end


function show_function_info( src, event, gui )
%
% display information about the currently selected function in a
% separate window with a multiline edit component. 
% The help information is taken from the gui.finfo data, loaded
% from the 't1runner.mat' file (which can be re-created using the
% maket1help function).
  try
    findex = get( gui.flist, 'Value' )
    fname  = char( gui.info.fnames( findex ))
    fhelp  = eval( [ 'gui.info.functions.' fname ] )
    % disp( fhelp );
  catch
    disp( lasterr );
  end
  
  %  display window with info
  fh = figure;
  u  = uicontrol( 'Style', 'edit', ...
                    'Max', 2, 'Min', 0, ...
                    'Units', 'normalized', ...
                    'Position', [0 0 1 1], ...
                    'HorizontalAlignment', 'left', ...
                    'FontSize', 12, ...
                    'FontName',  'FixedWidth' ...
                 );
  title( ['Information zur Funktion ' fname] );
  try
    helpstr = sprintf( 'Funktion: %s\n  nargin: %d\n nargout: %d\n\n%s\n\n', ...
                       fname, fhelp.nargin, fhelp.nargout, fhelp.helptext );
    set( u, 'String', helpstr );
  catch
    disp( lasterr );
    set( u, 'String', 'Hilfe leider nicht verfügbar.' );
  end
end % show_function_info




function run_button_fcn( src, event, gui ) 
  if (gui.debug == 1) disp( 'run_button_fcn...' ); end

  command = get( gui.cmdtf, 'String' );
  execute_command( command );
end


function execute_command( command )
  disp( [ '### execute_command <' command '>...' ] );

  try
    result  = eval( command );
    result2fig( result );
  catch
    errmsg = lasterr;
    if (strfind( errmsg, 'Too many output arguments.'))
      try 
        t1eval( command );
      catch
        disp( 'Interner Fehler in execute_function: ' );
        disp( lasterr );
      end
    else 
      disp( 'Interner Fehler in execute_function: ' );
      disp( lasterr );
    end

  end
end



% create a top-level menu on the menu-bar
function m = cmenu( label ) 
  m = uimenu( 'Label', label );
end


% create a menu item on the given menu
function mi = cmi( menu, label, accel, callback, enable )
  if (nargin < 5) enable = 'on'; else enable = 'off'; end;
  try 
    accel_char = accel(1);  
  catch
    accel_char = '';
  end

  mi = uimenu( menu, 'Label', label, ...
               'Accelerator', accel_char, ...
               'Callback',  callback, ...
               'Enable', enable ...
             );
end


% create a menu separator - above the given menuitem
function cmseparator( menuitem )
  set( menuitem, 'Separator', 'on' );
end



function doQuit( src, event, gui ) 
  disp( 'doQuit...' );
  close all;
  close( gcf );
end

function doCloseAll( src, event, gui )
  disp( 'doCloseAll: FIXME: should NOT delete the main window figure.' );
  % close all; 
  msvdelfig;
end

function doSave( src, event, gui )
  disp( 'doSave: not implemented, sorry.' );
end

function doSaveAs( src, event, gui ) 
  disp( 'doSaveAs: not implemented, sorry.' );
end

function doCopy( src, event, gui )
  disp( 'doCopy: not implemented, sorry.' );
end

function doCut( src, event, gui )
  disp( 'doCut: not implemented, sorry.' );
end

function doPaste( src, event, gui )
  disp( 'doPaste: not implemented, sorry.' );
end

function doDemo( src, event, gui )
  disp( 'doDemo: not implemented, sorry.' );
  disp( gui );

  t = timer( 'Name', 'DemoTimer', ...
             'StartDelay', 1, ...
             'ExecutionMode', 'FixedSpacing', ...
             'TasksToExecute', 10, ...
             'TimerFcn', {@timer_callback_fcn,gui}, ...
             'Period', 5.0 );
  disp( 'alive' );
  start( t );
  disp( 'timer started...' );
end


function timer_callback_fcn( obj, event, gui ) 
  disp( '### timer_callback_fcn...' );

  n_functions = length( gui.info.fnames );
  index       = ceil( n_functions*rand );
  command = gui.flist_abc{index};
  execute_command( command );
end


function doStartServer( src, event, gui )
  disp( 'doStartServer: not implemented, sorry.' );

  if (gui.socket == [] )  
    gui.port   = 6942;  
    gui.socket = pnet( 'tcpsocket', gui.port );
  else
    disp( 'doStartServer: ignored, socket already open: ' );
    disp( gui.socket );
  end
end


function doStopServer( src, event, gui )
  disp( 'doStopServer: not implemented, sorry.' );

  if (gui.socket == [] )  
    disp( 'doStopServer: no server running, ignored...' );
  else
    pnet( gui.socket, 'close' );
    gui.socket = [];
    disp( 'doStopServer: socket closed.' );
  end
end


function doShowAbout( src, event, gui )
  disp( 'doShowAbout: t1shell interactive environment' );
  disp( '(C) 2004 F.N.Hendrich, K.v.d.Heide' );

  text = { 'T1-Shell: Interaktive Matlab-Funktionen'; ...
           'zur Vorlesung Technische Informatik I im WS2004.'; ...
           ' '; ...
           '(C) 2004 K.v.d.Heide, F.N.Hendrich'; ...
           'Universität Hamburg, Fachbereich Informatik.'; ...
           'http://tams-www.informatik.uni-hamburg.de/'; ...
           ' ' ...
         };
  helpdlg( text, 'Über T1-Shell' );
end

function doShowUsage( src, event, gui )
  disp( 'Usage: select and double-click function' );
  disp( 'Or select function, edit command line, execute function' );
  text = { 'Die T1-Shell erlaubt den einfachen und direkten Zugriff'; ...
'auf alle Matlab-Funktionen aus dem Skript zur Vorlesung'; ...
'Technische Informatik I (im WS 2004/2005)'; ...
' '; ...
'Schritt 1:'; ...
'Die gewünschte Funktion im Listenfeld auswählen; um eine'; ...
'bestimmte Funktion schnell finden zu können, lassen sich die'; ...
'Funktionen entweder alphabetisch oder entsprechend der'; ...
'Kapitelstruktur im Skript anordnen. '; ...
' '; ...
'Schritt 2:'; ...
'Einen Eintrag im Listenfeld doppelklicken, um die jeweilige'; ...
'Funktion direkt auszuführen. Die Resultate werden in separaten'; ...
'Graphik- oder Textfenstern angezeigt.' ; ...
' '; ...
'Schritt 3:'; ...
'Die im Listenfeld ausgewählte Funktion wird mitsamt ihren'; ...
'Parametern in das Kommandozeilen-Textfeld übertragen.'; ...
'Dort können die Parameter nach Wunsch editiert werden; dann'; ...
'den [Funktion ausführen] Knopf anklicken, um die Funktion'; ...
'mit den gewählten Parametern auszuführen.' ; ...
' '; ...
'Hilfe:' ; ...
'Den [Infos zur Funktion] Knopf anklicken, um Hilfe zur aus-'; ...
'gewählten Funktion anzuzeigen.' };
  helpdlg( text, 'Benutzung der T1-Shell:' );
end




function doShowLicense( src, event, gui )
% based on dj5hg_info
% generates information window for Matlab programs designed by DJ5HG,
% usage as ButtonDownFcn or KeyPressFcn 

cc = double(get(gcf,'CurrentCharacter'));
if ~isempty(cc)
   switch cc
      case  27,  msvdelfig;   return
      case 127,  delete(gcf); return
   end
end
fh = findobj('type','figure','name','License');
if ~isempty(fh)
   delete(fh)
else
   fh = fullscreen('License',0.7*[1 1 1],0,[3 3; 2 2]);
   set(fh,'WindowButtonDownFcn','delete(gcf)','KeyPressFcn','delete(gcf)')
   axes('Position',[0 0 1 1])
   text(0,0,'t1shell/t1server License information:','HorizontalAlignment','center','FontSize',12)
   %text(0,0,'This program is designed by','HorizontalAlignment','center','FontSize',12)
   %text(0,-3.4,'Klaus von der Heide','HorizontalAlignment','center','FontSize',12,'Color','b','FontWeight','bold')
   %text(0,-4.8,'v.d.heide@on-line.de','HorizontalAlignment','center','FontSize',10,'Color','b','FontWeight','bold')
   text(0,-7.3,'programmed with','HorizontalAlignment','center','FontSize',10)
   text(0,-8.5,'MATLAB®.© 1984-2004 The Mathworks, Inc.','HorizontalAlignment','center','FontSize',10)
   text(0,-9.5,'This program may freely be used and distributed','HorizontalAlignment','center','FontSize',10)
   text(0,-10.5,'under the license conditions given in the file','HorizontalAlignment','center','FontSize',10)
   text(0,-11.5,'"license.txt" provided with the Matlab Component Runtime','HorizontalAlignment','center','FontSize',10)
   axis([-10 +10 -13 1.5])
   axis off
end

end
