/*
 * Generate the changes for a specific list of doc ID's, only documents accesible to the user will generate
 * results
 */
func (h *handler) sendChangesForDocIds(userChannels base.Set, explicitDocIds []string, options db.ChangesOptions) (error, bool) {

	// Subroutine that creates a response row for a document:
	first := true
	var lastSeq uint64 = 0
	//rowMap := make(map[uint64]*changesRow)
	rowMap := make(map[uint64]*db.ChangeEntry)

	createRow := func(doc db.IDAndRev) *db.ChangeEntry {
		row := &db.ChangeEntry{ID: doc.DocID}

		// Fetch the document body and other metadata that lives with it:
		body, _, _, _, flags, sequence, err := h.db.GetRevAndChannels(doc.DocID, doc.RevID, false)
		if err != nil {
			base.LogTo("Changes", "Unable to get changes for docID %v", doc.DocID)
			return nil
		}

		if sequence <= options.Since.Seq {
			return nil
		}

		if body == nil {
			return nil
		}

		doc.RevID = body["_rev"].(string)
		doc.Sequence = sequence

		changes := make([]db.ChangeRev, 1)
		changes[0] = db.ChangeRev{"rev": doc.RevID}
		row.Changes = changes
		row.Seq = db.SequenceID{Seq: doc.Sequence}
		row.SetBranched((flags & channels.Branched) != 0)

		if options.IncludeDocs || options.Conflicts {
			h.db.AddDocToChangeEntry(row, options)
		}

		return row
	}

	h.setHeader("Content-Type", "application/json")
	h.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store")
	h.response.Write([]byte("{\"results\":[\r\n"))

	var keys base.Uint64Slice

	for _, docID := range explicitDocIds {
		row := createRow(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0})
		if row != nil {
			rowMap[row.Seq.Seq] = row
			keys = append(keys, row.Seq.Seq)
		}
	}

	//Write out rows sorted by sequenceID
	keys.Sort()
	for _, k := range keys {
		if first {
			first = false
		} else {
			h.response.Write([]byte(","))
		}
		h.addJSON(rowMap[k])

		lastSeq = k

		if options.Limit > 0 {
			options.Limit--
			if options.Limit == 0 {
				break
			}
		}
	}

	s := fmt.Sprintf("],\n\"last_seq\":%d}\n", lastSeq)
	h.response.Write([]byte(s))
	h.logStatus(http.StatusOK, "OK")
	return nil, false
}
/*
 * Generate the changes for a specific list of doc ID's, only documents accesible to the user will generate
 * results
 */
func (h *handler) sendChangesForDocIds(userChannels base.Set, explicitDocIds []string, options db.ChangesOptions) (error, bool) {

	// Subroutine that creates a response row for a document:
	first := true
	var lastSeq uint64 = 0
	//rowMap := make(map[uint64]*changesRow)
	rowMap := make(map[uint64]*db.ChangeEntry)

	createRow := func(doc db.IDAndRev) *db.ChangeEntry {
		row := &db.ChangeEntry{ID: doc.DocID}

		// Fetch the document body and other metadata that lives with it:
		populatedDoc, body, err := h.db.GetDocAndActiveRev(doc.DocID)
		if err != nil {
			base.LogTo("Changes", "Unable to get changes for docID %v, caused by %v", doc.DocID, err)
			return nil
		}

		if populatedDoc.Sequence <= options.Since.Seq {
			return nil
		}

		if body == nil {
			return nil
		}

		changes := make([]db.ChangeRev, 1)
		changes[0] = db.ChangeRev{"rev": body["_rev"].(string)}
		row.Changes = changes
		row.Seq = db.SequenceID{Seq: populatedDoc.Sequence}
		row.SetBranched((populatedDoc.Flags & ch.Branched) != 0)

		var removedChannels []string

		if deleted, _ := body["_deleted"].(bool); deleted {
			row.Deleted = true
		}

		userCanSeeDocChannel := false

		if h.user == nil || h.user.Channels().Contains(ch.UserStarChannel) {
			userCanSeeDocChannel = true
		} else if len(populatedDoc.Channels) > 0 {
			//Do special _removed/_deleted processing
			for channel, removal := range populatedDoc.Channels {
				//Doc is tagged with channel or was removed at a sequence later that since sequence
				if removal == nil || removal.Seq > options.Since.Seq {
					//if the current user has access to this channel
					if h.user.CanSeeChannel(channel) {
						userCanSeeDocChannel = true
						//If the doc has been removed
						if removal != nil {
							removedChannels = append(removedChannels, channel)
							if removal.Deleted {
								row.Deleted = true
							}
						}
					}
				}
			}
		}

		if !userCanSeeDocChannel {
			return nil
		}

		row.Removed = base.SetFromArray(removedChannels)
		if options.IncludeDocs || options.Conflicts {
			h.db.AddDocInstanceToChangeEntry(row, populatedDoc, options)
		}

		return row
	}

	h.setHeader("Content-Type", "application/json")
	h.setHeader("Cache-Control", "private, max-age=0, no-cache, no-store")
	h.response.Write([]byte("{\"results\":[\r\n"))

	var keys base.Uint64Slice

	for _, docID := range explicitDocIds {
		row := createRow(db.IDAndRev{DocID: docID, RevID: "", Sequence: 0})
		if row != nil {
			rowMap[row.Seq.Seq] = row
			keys = append(keys, row.Seq.Seq)
		}
	}

	//Write out rows sorted by sequenceID
	keys.Sort()
	for _, k := range keys {
		if first {
			first = false
		} else {
			h.response.Write([]byte(","))
		}
		h.addJSON(rowMap[k])

		lastSeq = k

		if options.Limit > 0 {
			options.Limit--
			if options.Limit == 0 {
				break
			}
		}
	}

	s := fmt.Sprintf("],\n\"last_seq\":%d}\n", lastSeq)
	h.response.Write([]byte(s))
	h.logStatus(http.StatusOK, "OK")
	return nil, false
}