# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

from __future__ import print_function
import ctypes
import numpy

c_float_ptr = ctypes.POINTER(ctypes.c_float)
c_int_ptr = ctypes.POINTER(ctypes.c_int)
c_void_p = ctypes.c_void_p
c_int = ctypes.c_int
c_char_p = ctypes.c_char_p
c_float = ctypes.c_float

kaldi = ctypes.cdll.LoadLibrary("libkaldi-python-wrap.so")  # this needs to be in LD_LIBRARY_PATH

def decl(f, restype, argtypes):
    f.restype = restype
    if argtypes is not None and len(argtypes) != 0:
        f.argtypes = argtypes

decl(kaldi.Foo_new, c_void_p, [])
decl(kaldi.Foo_bar, None, [c_void_p])
decl(kaldi.Foo_getx, c_float_ptr, [c_void_p])
decl(kaldi.Foo_sizex, c_int, [c_void_p])

decl(kaldi.SBFMReader_new,          c_void_p,   [])
decl(kaldi.SBFMReader_new_char,     c_void_p,   [c_char_p])
decl(kaldi.SBFMReader_Open,         c_int,      [c_void_p, c_char_p])
decl(kaldi.SBFMReader_Done,         c_int,      [c_void_p])
decl(kaldi.SBFMReader_Key,          c_char_p,   [c_void_p])
decl(kaldi.SBFMReader_FreeCurrent,  None,       [c_void_p])
decl(kaldi.SBFMReader_Value,        c_void_p,   [c_void_p])
decl(kaldi.SBFMReader_Next,         None,       [c_void_p])
decl(kaldi.SBFMReader_IsOpen,       c_int,      [c_void_p])
decl(kaldi.SBFMReader_Close,        c_int,      [c_void_p])
decl(kaldi.SBFMReader_Delete,       None,       [c_void_p])

decl(kaldi.MatrixF_NumRows,     c_int,       [c_void_p])
decl(kaldi.MatrixF_NumCols,     c_int,       [c_void_p])
decl(kaldi.MatrixF_Stride,      c_int,       [c_void_p])
decl(kaldi.MatrixF_cpy_to_ptr,  None,        [c_void_p, c_float_ptr, c_int])
decl(kaldi.MatrixF_SizeInBytes, c_int,       [c_void_p])
decl(kaldi.MatrixF_Data,        c_float_ptr, [c_void_p])

if __name__ == "__main__":
    print("-------- Foo class example --------")
    a = kaldi.Foo_new()
    print("Calling Foo_bar(): ",)
    kaldi.Foo_bar(a)
    print()
    print("Result of Foo_getx(): ", kaldi.Foo_getx(a))
    print("Result of Foo_sizex(): ", kaldi.Foo_sizex(a))

    print()
    print("-------- Kaldi SBFMReader and MatrixF class example --------")

    reader = kaldi.SBFMReader_new_char("scp:data.scp")

    # data.scp has exactly one utterance, assert it's there
    assert(not kaldi.SBFMReader_Done(reader))

    utt_id = kaldi.SBFMReader_Key(reader)

    feat_value = kaldi.SBFMReader_Value(reader)
    feat_rows = kaldi.MatrixF_NumRows(feat_value)
    feat_cols = kaldi.MatrixF_NumCols(feat_value)
    feat_data = kaldi.MatrixF_Data(feat_value)

    # never use numpy.ndarray(buf=) or numpy.ctypeslib.as_array
    # because you don't know if Python or C owns buffer
    # (even if you numpy.copy() resulting array)
    # http://stackoverflow.com/questions/4355524/getting-data-from-ctypes-array-into-numpy
    #
    # Can't use memmove/memcpy because arrays are strided
    # Use cpy_to_ptr
    feats = numpy.empty((feat_rows,feat_cols), dtype=numpy.float32)

    # MUST: cast Python int to pointer, otherwise C interprets as 32-bit
    # if you print the pointer value before casting, you might see weird value before seg fault
    # casting fixes that
    feats_numpy_ptr = ctypes.cast(feats.ctypes.data, c_float_ptr)
    kaldi.MatrixF_cpy_to_ptr(feat_value, feats_numpy_ptr, feats.strides[0]/4)

    print("Read utterance:")
    print("  ID: ", utt_id)
    print("  Rows: ", feat_rows)
    print("  Cols: ", feat_cols)
    print("  Value: ", feat_data)
    print(feats)
    print("  This should match data.txt")

    # assert no more utterances left
    kaldi.SBFMReader_Next(reader)
    assert(kaldi.SBFMReader_Done(reader))

    kaldi.SBFMReader_Delete(reader)
