MOBI_RET mobi_xml_write_spine(xmlTextWriterPtr writer, const MOBIRawml *rawml) {
    if (!rawml || !rawml->resources || !rawml->markup || !writer) {
        return MOBI_INIT_FAILED;
    }
    /* get toc id */
    char ncxid[13 + 1];
    MOBIPart *curr = rawml->resources;
    while (curr != NULL && curr->type != T_NCX) {
        curr = curr->next;
    }
    if (curr) {
        snprintf(ncxid, sizeof(ncxid), "resource%05zu", curr->uid);
    } else {
        return MOBI_DATA_CORRUPT;
    }
    int xml_ret;
    xml_ret = xmlTextWriterStartElement(writer, BAD_CAST "spine");
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine)\n", xml_ret);
        return MOBI_XML_ERR;
    }
    xml_ret = xmlTextWriterWriteAttribute(writer, BAD_CAST "toc", BAD_CAST ncxid);
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine toc: %s)\n", xml_ret, ncxid);
        return MOBI_XML_ERR;
    }
    char id[9 + 1];
    curr = rawml->markup;
    while (curr != NULL) {
        snprintf(id, sizeof(id), "part%05zu", curr->uid);
        xml_ret = xmlTextWriterStartElement(writer, BAD_CAST "itemref");
        if (xml_ret < 0) {
            debug_print("XML error: %i (itemref)\n", xml_ret);
            return MOBI_XML_ERR;
        }
        xml_ret = xmlTextWriterWriteAttribute(writer, BAD_CAST "idref", BAD_CAST id);
        if (xml_ret < 0) {
            debug_print("XML error: %i (idref: %s)\n", xml_ret, id);
            return MOBI_XML_ERR;
        }
        xml_ret = xmlTextWriterEndElement(writer);
        if (xml_ret < 0) {
            debug_print("XML error: %i (idref: %s)\n", xml_ret, id);
            return MOBI_XML_ERR;
        }
        curr = curr->next;
    }
    xml_ret = xmlTextWriterEndElement(writer);
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine)\n", xml_ret);
        return MOBI_XML_ERR;
    }
    return MOBI_SUCCESS;
}
MOBI_RET mobi_xml_write_spine(xmlTextWriterPtr writer, const MOBIRawml *rawml) {
    if (!rawml || !rawml->resources || !rawml->markup || !writer) {
        return MOBI_INIT_FAILED;
    }
     
    char ncxid[13 + 1];
    MOBIPart *curr = rawml->resources;
    while (curr != NULL && curr->type != T_NCX) {
        curr = curr->next;
    }
    if (curr) {
        snprintf(ncxid, sizeof(ncxid), "resource%05zu", curr->uid);
    } else {
        return MOBI_DATA_CORRUPT;
    }
    int xml_ret;
    xml_ret = xmlTextWriterStartElement(writer, BAD_CAST "spine");
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine)\n", xml_ret);
        return MOBI_XML_ERR;
    }
    xml_ret = xmlTextWriterWriteAttribute(writer, BAD_CAST "toc", BAD_CAST ncxid);
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine toc: %s)\n", xml_ret, ncxid);
        return MOBI_XML_ERR;
    }
    char id[9 + 1];
    curr = rawml->markup;
    while (curr != NULL) {
        snprintf(id, sizeof(id), "part%05zu", curr->uid);
        xml_ret = xmlTextWriterStartElement(writer, BAD_CAST "itemref");
        if (xml_ret < 0) {
            debug_print("XML error: %i (itemref)\n", xml_ret);
            return MOBI_XML_ERR;
        }
        xml_ret = xmlTextWriterWriteAttribute(writer, BAD_CAST "idref", BAD_CAST id);
        if (xml_ret < 0) {
            debug_print("XML error: %i (idref: %s)\n", xml_ret, id);
            return MOBI_XML_ERR;
        }
        xml_ret = xmlTextWriterEndElement(writer);
        if (xml_ret < 0) {
            debug_print("XML error: %i (idref: %s)\n", xml_ret, id);
            return MOBI_XML_ERR;
        }
        curr = curr->next;
    }
    xml_ret = xmlTextWriterEndElement(writer);
    if (xml_ret < 0) {
        debug_print("XML error: %i (spine)\n", xml_ret);
        return MOBI_XML_ERR;
    }
    return MOBI_SUCCESS;
}