/* Autogenerated with DRAKON Editor 1.23 */
#include "m2m.h"

#include <memory.h>
#include <stdio.h>






struct m2m {
    tobject base;
    array_table employee;
    hashtable* employee_by_name;
    array_table department;
    hashtable* department_by_title;
    array_table company;
    int error_code;
};
static type_info_t m2m_t = {
	OBJECT_SIGNATURE,
	"m2m",
	(destructor_fun)m2m_destroy
};
struct employee {
    row_base _row;
    string8* name;
    company* company;
    obj_list* departments;
};
struct department {
    row_base _row;
    string8* title;
    company* company;
    obj_list* employees;
};
struct company {
    row_base _row;
    obj_list* departments;
    obj_list* employees;
};


static int
delete_generic(m2m* db,
    obj_list* deletion_list);


static int
ensure_can_delete(m2m* db,
    void* record,
    obj_list* deletion_list);


static void
do_delete(m2m* db,
    void* record,
    obj_list* deletion_list,
    int unlink);


static int
record_destructor(void* record,
    void* dbv);


static int
employee_pre_delete(m2m* db,
    employee* record,
    obj_list* deletion_list,
    int master);


static int
employee_ensure_can_delete(m2m* db,
    employee* record,
    obj_list* deletion_list);


static void
employee_do_delete(m2m* db,
    employee* record,
    obj_list* deletion_list,
    int unlink);


static unsigned int
employee_by_name_hash(const void* vobj);


static int
employee_by_name_eq(const void* lobj,
    const void* robj);


static int
department_pre_delete(m2m* db,
    department* record,
    obj_list* deletion_list,
    int master);


static int
department_ensure_can_delete(m2m* db,
    department* record,
    obj_list* deletion_list);


static void
department_do_delete(m2m* db,
    department* record,
    obj_list* deletion_list,
    int unlink);


static unsigned int
department_by_title_hash(const void* vobj);


static int
department_by_title_eq(const void* lobj,
    const void* robj);


static int
company_pre_delete(m2m* db,
    company* record,
    obj_list* deletion_list,
    int master);


static int
company_ensure_can_delete(m2m* db,
    company* record,
    obj_list* deletion_list);


static void
company_do_delete(m2m* db,
    company* record,
    obj_list* deletion_list,
    int unlink);


m2m* /* own */
m2m_create(void) {
    m2m* me = allocate_memory(sizeof(m2m));
    me->base.type = &m2m_t;
    array_table_init(&me->employee, "employee");
    me->employee.do_delete = (dd_fun)employee_do_delete;
    me->employee.ensure_can_delete = (ensure_cd_fun)employee_ensure_can_delete;
    me->employee_by_name = hashtable_create(0, employee_by_name_hash, employee_by_name_eq);
    array_table_init(&me->department, "department");
    me->department.do_delete = (dd_fun)department_do_delete;
    me->department.ensure_can_delete = (ensure_cd_fun)department_ensure_can_delete;
    me->department_by_title = hashtable_create(0, department_by_title_hash, department_by_title_eq);
    array_table_init(&me->company, "company");
    me->company.do_delete = (dd_fun)company_do_delete;
    me->company.ensure_can_delete = (ensure_cd_fun)company_ensure_can_delete;
    return me;
}


void
m2m_destroy(m2m* me /* own. null */) {
    if (!me) return;
    ENSURE(me->base.type == &m2m_t)
    hashtable_destroy(me->employee_by_name);
    m2m_foreach_employee(
        me,
        (employee_fun)record_destructor,
        me);
    array_table_cleanup(&me->employee);
    hashtable_destroy(me->department_by_title);
    m2m_foreach_department(
        me,
        (department_fun)record_destructor,
        me);
    array_table_cleanup(&me->department);
    m2m_foreach_company(
        me,
        (company_fun)record_destructor,
        me);
    array_table_cleanup(&me->company);
    free_memory(me, sizeof(m2m));
}


int
m2m_error(const m2m* db) {
    return db->error_code;
}


int
employee_id(const employee* me) {
    return me->_row.id;
}


employee* /* null */
m2m_get_employee(m2m* db,
    int rec_id) {
    if (rec_id < 0) return 0;
    if (rec_id >= db->employee.next) return 0;
    return db->employee.items[rec_id];
}


int
employee_departments_count(const employee* record) {
    return obj_list_length(record->departments);
}


department*
employee_departments(employee* record,
    int index) {
    return obj_list_get(record->departments, index);
}


int
m2m_set_employee_company(m2m* db,
    employee* _record_,
    company* new_value /* null */) {
    ENSURE(_record_ == m2m_get_employee(db, _record_->_row.id))
    if (_record_->company == new_value) {
        return 1;
    }
    if (new_value) {
        ENSURE(new_value == m2m_get_company(db, new_value->_row.id))
    }
    if (_record_->company != 0 ) {
        obj_list_remove(_record_->company->employees, _record_);
    }
    _record_->company = new_value;
    if (_record_->company != 0 ) {
        obj_list_add(_record_->company->employees, _record_);
    }
    return 1;
}


employee* /* null */
m2m_insert_employee(m2m* db,
    string8* name /* own. null */) {
    employee* _record_;
    int rec_id;
    employee _key_;
    employee* found;
    _record_ = 0;
    _key_.name = name;
    found = hashtable_get(db->employee_by_name, &_key_);
    if (found && found != _record_) {
        db->error_code = 1;
        return 0;
    }
    _record_ = allocate_memory(sizeof(employee));
    rec_id = array_table_insert(&db->employee, _record_);
    _record_->_row.id = rec_id;
    _record_->_row.table = &db->employee;
    _record_->name = name;
    hashtable_put(db->employee_by_name, _record_);
    _record_->departments = obj_list_create(0);
    return _record_;
}


int
m2m_delete_employee(m2m* db,
    employee* record /* null */) {
    obj_list* deletion_list;
    int deleted = 0;
    if (!record) return 0;
    deletion_list = obj_list_create(0);
    ENSURE(record == m2m_get_employee(db, record->_row.id))
    if (employee_pre_delete(db, record, deletion_list, 0)) {
        deleted = delete_generic(db, deletion_list);
    }
    obj_list_destroy(deletion_list);
    return deleted;
}


int
m2m_employee_count(const m2m* db) {
    return db->employee.count;
}


int
m2m_foreach_employee(m2m* db,
    employee_fun visitor,
    void* user_data /* null */) {
    int i, n;
    employee* current;
    n = db->employee.next;
    for (i = 0; i < n; i++) {
        current = db->employee.items[i];
        if (current) {
            if (visitor(current, user_data)) return 1;
        }
    }
    return 0;
}


employee* /* null */
m2m_employee_by_name(m2m* db,
    const string8* name /* null */) {
    employee _key_;
    _key_.name = (string8*)name;
    return hashtable_get(db->employee_by_name, &_key_);
}


int
department_id(const department* me) {
    return me->_row.id;
}


department* /* null */
m2m_get_department(m2m* db,
    int rec_id) {
    if (rec_id < 0) return 0;
    if (rec_id >= db->department.next) return 0;
    return db->department.items[rec_id];
}


int
department_employees_count(const department* record) {
    return obj_list_length(record->employees);
}


employee*
department_employees(department* record,
    int index) {
    return obj_list_get(record->employees, index);
}


int
m2m_set_department_company(m2m* db,
    department* _record_,
    company* new_value /* null */) {
    ENSURE(_record_ == m2m_get_department(db, _record_->_row.id))
    if (_record_->company == new_value) {
        return 1;
    }
    if (new_value) {
        ENSURE(new_value == m2m_get_company(db, new_value->_row.id))
    }
    if (_record_->company != 0 ) {
        obj_list_remove(_record_->company->departments, _record_);
    }
    _record_->company = new_value;
    if (_record_->company != 0 ) {
        obj_list_add(_record_->company->departments, _record_);
    }
    return 1;
}


department* /* null */
m2m_insert_department(m2m* db,
    string8* title /* own. null */) {
    department* _record_;
    int rec_id;
    department _key_;
    department* found;
    _record_ = 0;
    _key_.title = title;
    found = hashtable_get(db->department_by_title, &_key_);
    if (found && found != _record_) {
        db->error_code = 1;
        return 0;
    }
    _record_ = allocate_memory(sizeof(department));
    rec_id = array_table_insert(&db->department, _record_);
    _record_->_row.id = rec_id;
    _record_->_row.table = &db->department;
    _record_->title = title;
    hashtable_put(db->department_by_title, _record_);
    _record_->employees = obj_list_create(0);
    return _record_;
}


int
m2m_delete_department(m2m* db,
    department* record /* null */) {
    obj_list* deletion_list;
    int deleted = 0;
    if (!record) return 0;
    deletion_list = obj_list_create(0);
    ENSURE(record == m2m_get_department(db, record->_row.id))
    if (department_pre_delete(db, record, deletion_list, 0)) {
        deleted = delete_generic(db, deletion_list);
    }
    obj_list_destroy(deletion_list);
    return deleted;
}


int
m2m_department_count(const m2m* db) {
    return db->department.count;
}


int
m2m_foreach_department(m2m* db,
    department_fun visitor,
    void* user_data /* null */) {
    int i, n;
    department* current;
    n = db->department.next;
    for (i = 0; i < n; i++) {
        current = db->department.items[i];
        if (current) {
            if (visitor(current, user_data)) return 1;
        }
    }
    return 0;
}


department* /* null */
m2m_department_by_title(m2m* db,
    const string8* title /* null */) {
    department _key_;
    _key_.title = (string8*)title;
    return hashtable_get(db->department_by_title, &_key_);
}


int
company_id(const company* me) {
    return me->_row.id;
}


company* /* null */
m2m_get_company(m2m* db,
    int rec_id) {
    if (rec_id < 0) return 0;
    if (rec_id >= db->company.next) return 0;
    return db->company.items[rec_id];
}


int
company_departments_count(const company* record) {
    return obj_list_length(record->departments);
}


department*
company_departments(company* record,
    int index) {
    return obj_list_get(record->departments, index);
}


int
company_employees_count(const company* record) {
    return obj_list_length(record->employees);
}


employee*
company_employees(company* record,
    int index) {
    return obj_list_get(record->employees, index);
}


company* /* null */
m2m_insert_company(m2m* db) {
    company* _record_;
    int rec_id;
    _record_ = 0;
    _record_ = allocate_memory(sizeof(company));
    rec_id = array_table_insert(&db->company, _record_);
    _record_->_row.id = rec_id;
    _record_->_row.table = &db->company;
    _record_->departments = obj_list_create(0);
    _record_->employees = obj_list_create(0);
    return _record_;
}


int
m2m_delete_company(m2m* db,
    company* record /* null */) {
    obj_list* deletion_list;
    int deleted = 0;
    if (!record) return 0;
    deletion_list = obj_list_create(0);
    ENSURE(record == m2m_get_company(db, record->_row.id))
    if (company_pre_delete(db, record, deletion_list, 0)) {
        deleted = delete_generic(db, deletion_list);
    }
    obj_list_destroy(deletion_list);
    return deleted;
}


int
m2m_company_count(const m2m* db) {
    return db->company.count;
}


int
m2m_foreach_company(m2m* db,
    company_fun visitor,
    void* user_data /* null */) {
    int i, n;
    company* current;
    n = db->company.next;
    for (i = 0; i < n; i++) {
        current = db->company.items[i];
        if (current) {
            if (visitor(current, user_data)) return 1;
        }
    }
    return 0;
}


void
m2m_add_department_employees(m2m* db,
    department* left,
    employee* right) {
    if (left == 0 || right == 0) return;
    ENSURE(left == m2m_get_department(db, left->_row.id))
    ENSURE(right == m2m_get_employee(db, right->_row.id))
    obj_list_add(left->employees, right);
    obj_list_add(right->departments, left);
}


void
m2m_remove_department_employees(m2m* db,
    department* left,
    employee* right) {
    if (left == 0 || right == 0) return;
    ENSURE(left == m2m_get_department(db, left->_row.id))
    ENSURE(right == m2m_get_employee(db, right->_row.id))
    obj_list_remove(left->employees, right);
    obj_list_remove(right->departments, left);
}


static int
delete_generic(m2m* db,
    obj_list* deletion_list) {
    void* record;
    int i, n;
    n = obj_list_length(deletion_list);
    for (i = 0; i < n; i++) {
        record = obj_list_get(deletion_list, i);
        if (!ensure_can_delete(db, record, deletion_list)) {
            return 0;
        }
    }
    for (i = 0; i < n; i++) {
        record = obj_list_get(deletion_list, i);
        do_delete(db, record, deletion_list, 1);
    }
    return 1;
}


static int
ensure_can_delete(m2m* db,
    void* record,
    obj_list* deletion_list) {
    row_base* row = record;
    return row->table->ensure_can_delete(
        db,
        record,
        deletion_list);
}


static void
do_delete(m2m* db,
    void* record,
    obj_list* deletion_list,
    int unlink) {
    row_base* row = record;
    row->table->do_delete(
        db,
        record,
        deletion_list,
        unlink);
}


static int
record_destructor(void* record,
    void* dbv) {
    m2m* db = dbv;
    do_delete(
        db,
        record,
        0,
        0);
    return 0;
}


static int
employee_pre_delete(m2m* db,
    employee* record,
    obj_list* deletion_list,
    int master) {
    if (obj_list_contains(deletion_list, record)) {
        return 1;
    } else {
        obj_list_add(deletion_list, record);
    }
    return 1;
}


static int
employee_ensure_can_delete(m2m* db,
    employee* record,
    obj_list* deletion_list) {
    return 1;
}


static void
employee_do_delete(m2m* db,
    employee* record,
    obj_list* deletion_list,
    int unlink) {
    int i, length;
    void* _that_;
    department* _that_departments;
    if (unlink) {
        if (record->company != 0 ) {
            if ( !obj_list_contains(deletion_list, record->company)) {
                obj_list_remove(record->company->employees, record);
            }
        }
        length = obj_list_length(record->departments);
        for (i = 0; i < length; i++) {
            _that_ = obj_list_get(record->departments, i);
            _that_departments = _that_;
            if ( !obj_list_contains(deletion_list, _that_departments)) {
                obj_list_remove(_that_departments->employees, record);
            }
        }
        hashtable_remove(db->employee_by_name, record);
        array_table_delete(&db->employee, record->_row.id);
    }
    string8_destroy(record->name);
    obj_list_destroy(record->departments);
    free_memory(record, sizeof(employee));
}


static unsigned int
employee_by_name_hash(const void* vobj) {
    const employee* obj;
    unsigned int code;
    if (!vobj) return 0;
    obj = vobj;
    code = string8_hash(obj->name);
    return code;
}


static int
employee_by_name_eq(const void* lobj,
    const void* robj) {
    const employee* left;
    const employee* right;
    left = lobj;
    right = robj;
    if (!string8_equal(left->name, right->name)) return 0;
    return 1;
}


static int
department_pre_delete(m2m* db,
    department* record,
    obj_list* deletion_list,
    int master) {
    if (obj_list_contains(deletion_list, record)) {
        return 1;
    } else {
        obj_list_add(deletion_list, record);
    }
    return 1;
}


static int
department_ensure_can_delete(m2m* db,
    department* record,
    obj_list* deletion_list) {
    return 1;
}


static void
department_do_delete(m2m* db,
    department* record,
    obj_list* deletion_list,
    int unlink) {
    int i, length;
    void* _that_;
    employee* _that_employees;
    if (unlink) {
        if (record->company != 0 ) {
            if ( !obj_list_contains(deletion_list, record->company)) {
                obj_list_remove(record->company->departments, record);
            }
        }
        length = obj_list_length(record->employees);
        for (i = 0; i < length; i++) {
            _that_ = obj_list_get(record->employees, i);
            _that_employees = _that_;
            if ( !obj_list_contains(deletion_list, _that_employees)) {
                obj_list_remove(_that_employees->departments, record);
            }
        }
        hashtable_remove(db->department_by_title, record);
        array_table_delete(&db->department, record->_row.id);
    }
    string8_destroy(record->title);
    obj_list_destroy(record->employees);
    free_memory(record, sizeof(department));
}


static unsigned int
department_by_title_hash(const void* vobj) {
    const department* obj;
    unsigned int code;
    if (!vobj) return 0;
    obj = vobj;
    code = string8_hash(obj->title);
    return code;
}


static int
department_by_title_eq(const void* lobj,
    const void* robj) {
    const department* left;
    const department* right;
    left = lobj;
    right = robj;
    if (!string8_equal(left->title, right->title)) return 0;
    return 1;
}


static int
company_pre_delete(m2m* db,
    company* record,
    obj_list* deletion_list,
    int master) {
    int length, i;
    void* _that_;
    if (obj_list_contains(deletion_list, record)) {
        return 1;
    } else {
        obj_list_add(deletion_list, record);
    }
    length = obj_list_length(record->departments);
    for (i = 0; i < length; i++) {
        _that_ = obj_list_get(record->departments, i);
        if (!department_pre_delete(db, _that_, deletion_list, 0)) return 0;
    }
    length = obj_list_length(record->employees);
    for (i = 0; i < length; i++) {
        _that_ = obj_list_get(record->employees, i);
        if (!employee_pre_delete(db, _that_, deletion_list, 0)) return 0;
    }
    return 1;
}


static int
company_ensure_can_delete(m2m* db,
    company* record,
    obj_list* deletion_list) {
    return 1;
}


static void
company_do_delete(m2m* db,
    company* record,
    obj_list* deletion_list,
    int unlink) {
    if (unlink) {
        array_table_delete(&db->company, record->_row.id);
    }
    obj_list_destroy(record->departments);
    obj_list_destroy(record->employees);
    free_memory(record, sizeof(company));
}
int main(
    int argc,
    char** argv
) {
    // item 327
    department* yellow_m;
    department* grey_m;
    employee* mark;
    employee* john;
    employee* scott;
    company* company;
    /* item 326 */
    m2m* db = m2m_create();
    /* item 252 */
    yellow_m = m2m_insert_department(db,
     string8_from_cstr("Yellow marketing", 20));
    grey_m = m2m_insert_department(db,
     string8_from_cstr("Grey marketing", 20));
    /* item 253 */
    mark = m2m_insert_employee(db,
     string8_from_cstr("Mark", 10));
    john = m2m_insert_employee(db,
     string8_from_cstr("John", 10));
    scott = m2m_insert_employee(db,
     string8_from_cstr("Scott", 10));
    /* item 188 */
    m2m_add_department_employees(db, yellow_m, mark);
    /* item 238 */
    m2m_add_department_employees(db, yellow_m, 0);
    /* item 212 */
    m2m_add_department_employees(db, grey_m, mark);
    m2m_add_department_employees(db, grey_m, john);
    m2m_add_department_employees(db, grey_m, scott);
    /* item 254 */
    ENSURE(3 == department_employees_count(grey_m))
    ENSURE(1 == department_employees_count(yellow_m))
    
    ENSURE(mark == department_employees(grey_m, 0))
    ENSURE(john == department_employees(grey_m, 1))
    ENSURE(scott == department_employees(grey_m, 2))
    
    ENSURE(mark == department_employees(yellow_m, 0))
    /* item 330 */
    ENSURE(2 == employee_departments_count(mark))
    ENSURE(1 == employee_departments_count(john))
    ENSURE(1 == employee_departments_count(scott))
    
    ENSURE(yellow_m == employee_departments(mark, 0))
    ENSURE(grey_m == employee_departments(mark, 1))
    ENSURE(grey_m == employee_departments(john, 0))
    ENSURE(grey_m == employee_departments(scott, 0))
    /* item 227 */
    company = m2m_insert_company(db);
    /* item 228 */
    ENSURE(m2m_set_employee_company(db, mark, company))
    ENSURE(m2m_set_employee_company(db, john, company))
    ENSURE(m2m_set_employee_company(db, scott, company))
    /* item 302 */
    ENSURE(m2m_set_department_company(db, grey_m, company))
    ENSURE(m2m_set_department_company(db, yellow_m, company))
    /* item 230 */
    m2m_remove_department_employees(db, yellow_m, mark);
    /* item 231 */
    m2m_remove_department_employees(db, grey_m, mark);
    m2m_remove_department_employees(db, grey_m, john);
    m2m_remove_department_employees(db, grey_m, scott);
    /* item 331 */
    ENSURE(0 == department_employees_count(grey_m))
    ENSURE(0 == department_employees_count(yellow_m))
    /* item 332 */
    ENSURE(0 == employee_departments_count(mark))
    ENSURE(0 == employee_departments_count(john))
    ENSURE(0 == employee_departments_count(scott))
    /* item 234 */
    m2m_add_department_employees(db, yellow_m, john);
    /* item 303 */
    m2m_add_department_employees(db, grey_m, mark);
    m2m_add_department_employees(db, grey_m, john);
    m2m_add_department_employees(db, grey_m, scott);
    /* item 333 */
    ENSURE(3 == department_employees_count(grey_m))
    ENSURE(1 == department_employees_count(yellow_m))
    
    ENSURE(mark == department_employees(grey_m, 0))
    ENSURE(john == department_employees(grey_m, 1))
    ENSURE(scott == department_employees(grey_m, 2))
    
    ENSURE(john == department_employees(yellow_m, 0))
    /* item 334 */
    ENSURE(1 == employee_departments_count(mark))
    ENSURE(2 == employee_departments_count(john))
    ENSURE(1 == employee_departments_count(scott))
    
    ENSURE(grey_m == employee_departments(mark, 0))
    ENSURE(yellow_m == employee_departments(john, 0))
    ENSURE(grey_m == employee_departments(john, 1))
    ENSURE(grey_m == employee_departments(scott, 0))
    /* item 239 */
    ENSURE(m2m_delete_employee(db, scott))
    /* item 335 */
    ENSURE(2 == department_employees_count(grey_m))
    ENSURE(1 == department_employees_count(yellow_m))
    
    ENSURE(mark == department_employees(grey_m, 0))
    ENSURE(john == department_employees(grey_m, 1))
    
    ENSURE(john == department_employees(yellow_m, 0))
    /* item 241 */
    ENSURE(m2m_delete_department(db, yellow_m))
    /* item 336 */
    ENSURE(1 == employee_departments_count(john))
    ENSURE(grey_m == employee_departments(john, 0))
    /* item 243 */
    ENSURE(m2m_delete_company(db, company))
    /* item 244 */
    ENSURE(0 == m2m_employee_count(db))
    ENSURE(0 == m2m_department_count(db))
    /* item 329 */
    object_destroy(db);
    /* item 328 */
    return 0;
    
}



