#!/usr/bin/python
""".DBF creation helpers.

Note: this is a legacy interface.  New code should use Dbf class
    for table creation (see examples in dbf.py)

TODO:
  - handle Memo fields.
  - check length of the fields accoring to the
    `http://www.clicketyclick.dk/databases/xbase/format/data_types.html`

"""
"""History (most recent first)
04-jul-2006 [als]   added export declaration;
                    updated for dbfpy 2.0
15-dec-2005 [yc]    define dbf_new.__slots__
14-dec-2005 [yc]    added vim modeline; retab'd; added doc-strings;
                    dbf_new now is a new class (inherited from object)
??-jun-2000 [--]    added by Hans Fiby
"""

__version__ = "$Revision: 1.4 $"[11:-2]
__date__ = "$Date: 2006/07/04 08:18:18 $"[7:-2]

__all__ = ["dbf_new"]

from dbf import *
from fields import *
from header import *
from record import *

class _FieldDefinition(object):
    """Field definition.

    This is a simple structure, which contains ``name``, ``type``,
    ``len``, ``dec`` and ``cls`` fields.

    Objects also implement get/setitem magic functions, so fields
    could be accessed via sequence iterface, where 'name' has
    index 0, 'type' index 1, 'len' index 2, 'dec' index 3 and
    'cls' could be located at index 4.

    """

    __slots__ = "name", "type", "len", "dec", "cls"

    # WARNING: be attentive - dictionaries are mutable!
    FLD_TYPES = {
        # type: (cls, len)
        "C": (DbfCharacterFieldDef, None),
        "N": (DbfNumericFieldDef, None),
        "L": (DbfLogicalFieldDef, 1),
        # FIXME: support memos
        # "M": (DbfMemoFieldDef),
        "D": (DbfDateFieldDef, 8),
        # FIXME: I'm not sure length should be 14 characters!
        # but temporary I use it, cuz date is 8 characters
        # and time 6 (hhmmss)
        "T": (DbfDateTimeFieldDef, 14),
    }

    def __init__(self, name, type, len=None, dec=0):
        _cls, _len = self.FLD_TYPES[type]
        if _len is None:
            if len is None:
                raise ValueError("Field length must be defined")
            _len = len
        self.name = name
        self.type = type
        self.len = _len
        self.dec = dec
        self.cls = _cls

    def getDbfField(self):
        "Return `DbfFieldDef` instance from the current definition."
        return self.cls(self.name, self.len, self.dec)

    def appendToHeader(self, dbfh):
        """Create a `DbfFieldDef` instance and append it to the dbf header.

        Arguments:
            dbfh: `DbfHeader` instance.

        """
        _dbff = self.getDbfField()
        dbfh.addField(_dbff)


class dbf_new(object):
    """New .DBF creation helper.

    Example Usage:

        dbfn = dbf_new()
        dbfn.add_field("name",'C',80)
        dbfn.add_field("price",'N',10,2)
        dbfn.add_field("date",'D',8)
        dbfn.write("tst.dbf")

    Note:
        This module cannot handle Memo-fields,
        they are special.

    """

    __slots__ = ("fields",)

    FieldDefinitionClass = _FieldDefinition

    def __init__(self):
        self.fields = []

    def add_field(self, name, typ, len, dec=0):
        """Add field definition.

        Arguments:
            name:
                field name (str object). field name must not
                contain ASCII NULs and it's length shouldn't
                exceed 10 characters.
            typ:
                type of the field. this must be a single character
                from the "CNLMDT" set meaning character, numeric,
                logical, memo, date and date/time respectively.
            len:
                length of the field. this argument is used only for
                the character and numeric fields. all other fields
                have fixed length.
                FIXME: use None as a default for this argument?
            dec:
                decimal precision. used only for the numric fields.

        """
        self.fields.append(self.FieldDefinitionClass(name, typ, len, dec))

    def write(self, filename):
        """Create empty .DBF file using current structure."""
        _dbfh = DbfHeader()
        _dbfh.setCurrentDate()
        for _fldDef in self.fields:
            _fldDef.appendToHeader(_dbfh)
        _dbfStream = file(filename, "wb")
        _dbfh.write(_dbfStream)
        _dbfStream.close()


if (__name__=='__main__'):
    # create a new DBF-File
    dbfn=dbf_new()
    dbfn.add_field("name",'C',80)
    dbfn.add_field("price",'N',10,2)
    dbfn.add_field("date",'D',8)
    dbfn.write("tst.dbf")
    # test new dbf
    print "*** created tst.dbf: ***"
    dbft = Dbf('tst.dbf', readOnly=0)
    print repr(dbft)
    # add a record
    rec=DbfRecord(dbft)
    rec['name']='something'
    rec['price']=10.5
    rec['date']=(2000,1,12)
    rec.store()
    # add another record
    rec=DbfRecord(dbft)
    rec['name']='foo and bar'
    rec['price']=12234
    rec['date']=(1992,7,15)
    rec.store()

    # show the records
    print "*** inserted 2 records into tst.dbf: ***"
    print repr(dbft)
    for i1 in range(len(dbft)):
        rec = dbft[i1]
        for fldName in dbft.fieldNames:
            print '%s:\t %s'%(fldName, rec[fldName])
        print
    dbft.close()

# vim: set et sts=4 sw=4 :
