"""
    MoinMoin - "PhoneDial" action
    =============================

    Copyright (c) 2002 by Thomas Waldmann <tw@waldmann-edv.de>
    All rights reserved, see COPYING for details.

    This is released under the Gnu General Public Licence. A copy of
    this can be found at http://www.opensource.org/licenses/gpl-license.html

    SerialLink class taken from PyGarmin (GPL), see http://pygarmin.sourceforge.net/

    So what is this thing doing and how it has to be configured ?
    =============================================================

    This action dials a phone number using an Eurit 40 ISDN telephone
    attached via a serial interface to the SERVERs COM ports.

    You maybe could also use a modem for dialing and then take the call
    with a phone attached to the same line. This action simply sends an
    ATD<phonenumber> to the device attached to the serial port.

    Add this to your moin_config.py:
    phonedial_serialdevice = "COM1"             (Win32 and untested)
    phonedial_serialdevice = "/dev/ttyS0"       (Linux)
    
    Add this to your ./data/intermap.txt (in your wiki):
    FON http://whatever/moin.cgi?action=PhoneDial&number=
    
    Add this to your pages (example):
    Ring my best friend: FON:0123456789
    
    After saving the page, the FON entry should get a link to the
    PhoneDial action. And if you click on it, it should dial the number
    using the device attached to the com port.

    BTW: these are my first footsteps in Python and MoinMoin Programming.
    
    So if you are more experienced concerning Python (and moinmoin), then
    please review this and please, if you improve it, send me a copy of your
    improvements. Thanks.

    Version History:
    
    20020110 first public version - "it works for me" 
    20020206 filtering out special characters like "-" or "/" for not
             confusing the Eurit phone
    
    Known Bugs:
    
    After clicking on a FON link, you will see the FrontPage.
    
    Known Limitations:
    
    It is the web server computer (where moin.cgi runs) that is dialling via
    the com port. If you work at a different PC far away from the server or
    if you have multiple phones you want to get dialling, you maybe need more
    than this script.
        
"""

# is this all needed ?:
import os, string, time, sys
from MoinMoin import config, util, wikiutil, webapi
from MoinMoin.Page import Page

# Now some practical implementations

class SerialLink:
   """
     A serial link will look something like this, though real
     implementations will probably override most of it.
   """
   def __init__(self, f, timeout = 5):
      self.f = f
      self.initserial()
      self.settimeout(timeout)

   def initserial(self):
      "Set up baud rate, handshaking, etc"
      pass
   
   def read(self, n):
      """
      Read n bytes and return them. Real implementations should
      raise a LinkException if there is a timeout > self.timeout
      """
      return self.f.read(n)

   def write(self, data):
      self.f.write(data)

   def settimeout(self, secs):
      self.timeout = secs
      
   def __del__(self):
      """Should close down any opened resources"""
      pass


class UnixSerialLink(SerialLink):

   def __init__(self, device):
      f = open(device, "w+", 0)
      SerialLink.__init__(self, f)

   def initserial(self):
      from tty import *
      
      fd = self.f.fileno()
      setraw(fd)
      mode = tcgetattr(fd)
      mode[ISPEED] = mode[OSPEED] = B2400
      # mode[LFLAG] = mode[LFLAG] | ECHO
      tcsetattr(fd, TCSAFLUSH, mode)

   def read(self, n):
      import select
      
      i = 0
      data = []
      while i < n:
         iset,oset,eset = select.select([self.f.fileno()], [], [], self.timeout)
         if iset == []:
           raise LinkException, "time out"
         b = self.f.read(1)
         data.append(b)
         i = i + 1
      return string.join(data,'')

   def __del__(self):
      self.f.close()

# Win32 Serial Link ==================================================

if os.name == 'nt':
   from win32file import * 
   import win32con

class Win32SerialLink(SerialLink):
   def __init__(self, device):
      self.device = device
      handle = CreateFile(device,
         win32con.GENERIC_READ | win32con.GENERIC_WRITE,
         0, # exclusive access
         None, # no security
         win32con.OPEN_EXISTING,
         0,
         None)
      SerialLink.__init__(self, handle)

   def initserial(self):
      # Remove anything that was there
      PurgeComm(self.f, PURGE_TXABORT | PURGE_RXABORT
         | PURGE_TXCLEAR | PURGE_RXCLEAR )

      # Setup the connection info.
      dcb = GetCommState( self.f )
      dcb.BaudRate = CBR_2400
      dcb.ByteSize = 8
      dcb.Parity = NOPARITY
      dcb.StopBits = ONESTOPBIT
      SetCommState(self.f, dcb)

   def read(self, n):
      buffer = AllocateReadBuffer(n)
      rc, data = ReadFile(self.f, buffer)
      if len(data) != n:
         raise LinkException, "time out";
      return data

   def write(self, n):
      rc,n = WriteFile(self.f, n)
      if rc:
         raise LinkException, "WriteFile error";

   def settimeout(self, secs):
      SerialLink.settimeout(self, secs)
      # Setup time-outs
      timeouts = 0xFFFFFFFF, 0, 1000*secs, 0, 1000*secs
      SetCommTimeouts(self.f, timeouts)

   def __del__(self):
      CloseHandle(self.f)

      
class Eurit:
   """
   A representation of the Eurit telephone, which is connected
   via some physical connection, typically a SerialLink of some sort.
   """
   def __init__(self):
      if os.name == 'nt':
         serialDevice =  config.phonedial_serialdevice
         self.link = Win32SerialLink(serialDevice)
      else:
         serialDevice =  config.phonedial_serialdevice
         self.link = UnixSerialLink(serialDevice)
   
   # === this is the important part, here we do the dialling:
   def dial(self, number):
      self.link.write("ATD" + number + "\r");
   # ========================================================


def execute(pagename, form):

    # get the phone number
    if form.has_key('number'):
        number = form['number'].value
    else:
        number = ""

    number = string.replace(number,'/','')
    number = string.replace(number,'-','')
    
    eurit = Eurit()
    eurit.dial(number)
    page = Page(pagename)
    page.send_page(form, msg="<b>Dialled " + number + ".</b>")
